diff options
Diffstat (limited to 'src/squelch/rule.cc')
-rw-r--r-- | src/squelch/rule.cc | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/src/squelch/rule.cc b/src/squelch/rule.cc new file mode 100644 index 00000000..1c17d2fd --- /dev/null +++ b/src/squelch/rule.cc @@ -0,0 +1,332 @@ +#include "tome/squelch/rule_fwd.hpp" +#include "tome/squelch/rule.hpp" + +#include "tome/squelch/cursor.hpp" +#include "tome/squelch/condition.hpp" +#include "tome/squelch/tree_printer.hpp" +#include "../angband.h" +#include "../modules.hpp" +#include "../object1.hpp" +#include "../object2.hpp" +#include "../object_type.hpp" +#include "../quark.hpp" +#include "../tables.hpp" +#include "../util.hpp" +#include "../variable.hpp" + +namespace squelch { + +EnumStringMap<action_type> &action_mapping() +{ + static auto m = new EnumStringMap<action_type> { + { action_type::AUTO_DESTROY, "destroy" }, + { action_type::AUTO_PICKUP, "pickup" }, + { action_type::AUTO_INSCRIBE, "inscribe" } }; + return *m; +} + +void Rule::set_name(const char *new_name) +{ + assert(new_name != nullptr); + m_name = new_name; +} + +const char *Rule::get_name() const +{ + return m_name.c_str(); +} + +std::shared_ptr<Condition> Rule::get_condition() const +{ + return m_condition; +} + +json_t *Rule::to_json() const +{ + json_t *rule_json = json_object(); + json_object_set_new(rule_json, + "name", + json_string(m_name.c_str())); + json_object_set_new(rule_json, + "action", + json_string(action_mapping().stringify(m_action))); + json_object_set_new(rule_json, + "module", + json_string(modules[m_module_idx].meta.name)); + json_object_set_new(rule_json, + "condition", + Condition::optional_to_json(m_condition)); + return rule_json; +} + +void Rule::add_new_condition(Cursor *cursor, + ConditionFactory const &factory) +{ + // Top-level condition? + if (!m_condition) + { + // Sanity check for navigation stack + assert(cursor->empty()); + + // Create new top-level condition + m_condition = factory(); + + // Select the condition + if (m_condition) + { + cursor->push(m_condition.get()); + } + } + else + { + cursor->current()->add_child(factory); + } +} + +void Rule::delete_selected_condition(Cursor *cursor) +{ + assert(cursor->size() >= 1); + + if (cursor->size() == 1) + { + cursor->pop(); + m_condition.reset(); + } + else + { + Condition *prev_top = cursor->pop(); + Condition *top = cursor->current(); + + // Jump up a level; this is a simple way to ensure a + // valid cursor. We could be a little cleverer here by + // trying to move inside the current level, but it + // gets a little complicated. + cursor->move_left(); + + // Now we can remove the condition from its parent + top->remove_child(prev_top); + } +} + +void Rule::write_tree(TreePrinter *tree_printer, Cursor *cursor) const +{ + // Write out the main rule + do_write_tree(tree_printer); + + // Write out the condition + if (m_condition) + { + m_condition->display(tree_printer, cursor); + } +} + +bool Rule::apply_rule(object_type *o_ptr, int item_idx) const +{ + // Check module + if (m_module_idx != game_module_idx) + { + return false; + } + + // Check condition + if (m_condition && m_condition->is_match(o_ptr)) + { + return do_apply_rule(o_ptr, item_idx); + } + + // Doesn't apply + return false; +} + +std::shared_ptr<Rule> Rule::parse_rule(json_t *rule_json) +{ + if (!json_is_object(rule_json)) + { + msg_print("Rule is not an object"); + return nullptr; + } + + // Retrieve the attributes + char *rule_name_s = nullptr; + char *rule_action_s = nullptr; + char *rule_module_s = nullptr; + if (json_unpack(rule_json, + "{s:s,s:s,s:s}", + "name", &rule_name_s, + "action", &rule_action_s, + "module", &rule_module_s) < 0) + { + msg_print("Rule missing required field(s)"); + return nullptr; + } + + // Convert attributes + action_type action; + if (!action_mapping().parse((cptr) rule_action_s, &action)) + { + msg_format("Invalid rule action '%s'", rule_action_s); + return nullptr; + } + + int module_idx = find_module((cptr) rule_module_s); + if (module_idx < 0) + { + msg_format("Skipping rule for unrecognized module '%s'", + (cptr) rule_module_s); + return nullptr; + } + + // Parse condition + std::shared_ptr<Condition> condition = + Condition::parse_condition(json_object_get(rule_json, "condition")); + + // Parse rule + switch (action) + { + case action_type::AUTO_INSCRIBE: + { + json_t *rule_inscription_j = json_object_get(rule_json, "inscription"); + + if (rule_inscription_j == nullptr) + { + msg_print("Inscription rule missing 'inscription' attribute"); + return nullptr; + } + if (!json_is_string(rule_inscription_j)) + { + msg_print("Inscription rule 'inscription' attribute wrong type"); + return nullptr; + } + + std::string inscription = + json_string_value(rule_inscription_j); + return std::make_shared<InscribeRule>( + rule_name_s, module_idx, condition, inscription); + } + + case action_type::AUTO_PICKUP: + return std::make_shared<PickUpRule>( + rule_name_s, module_idx, condition); + + case action_type::AUTO_DESTROY: + return std::make_shared<DestroyRule>( + rule_name_s, module_idx, condition); + } + + assert(false); + return nullptr; +} + + +void DestroyRule::do_write_tree(TreePrinter *p) const +{ + p->write(TERM_GREEN, "A rule named \""); + p->write(TERM_WHITE, m_name.c_str()); + p->write(TERM_GREEN, "\" to "); + p->write(TERM_L_GREEN, "destroy"); + p->write(TERM_GREEN, " when"); + p->write(TERM_WHITE, "\n"); +} + +bool DestroyRule::do_apply_rule(object_type *o_ptr, int item_idx) const +{ + // Must be identified + if (object_aware_p(o_ptr) == FALSE) + { + return false; + } + + // Never destroy inscribed items + if (o_ptr->note) + { + return false; + } + + // Ignore artifacts; cannot be destroyed anyway + if (artifact_p(o_ptr)) + { + return false; + } + + // Cannot destroy CURSE_NO_DROP objects. + { + u32b f1, f2, f3, f4, f5, esp; + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((f4 & TR4_CURSE_NO_DROP) != 0) + { + return false; + } + } + + // Destroy + msg_print("<Auto-destroy>"); + inc_stack_size(item_idx, -o_ptr->number); + return true; +} + +void PickUpRule::do_write_tree(TreePrinter *p) const +{ + p->write(TERM_GREEN, "A rule named \""); + p->write(TERM_WHITE, m_name.c_str()); + p->write(TERM_GREEN, "\" to "); + p->write(TERM_L_GREEN, "pick up"); + p->write(TERM_GREEN, " when"); + p->write(TERM_WHITE, "\n"); +} + +bool PickUpRule::do_apply_rule(object_type *o_ptr, int item_idx) const +{ + if (item_idx >= 0) + { + return false; + } + + if (!inven_carry_okay(o_ptr)) + { + return false; + } + + msg_print("<Auto-pickup>"); + object_pickup(-item_idx); + return true; +} + +json_t *InscribeRule::to_json() const +{ + json_t *j = Rule::to_json(); + + json_object_set_new(j, + "inscription", + json_string(m_inscription.c_str())); + + return j; +} + +void InscribeRule::do_write_tree(TreePrinter *p) const +{ + p->write(TERM_GREEN, "A rule named \""); + p->write(TERM_WHITE, m_name.c_str()); + p->write(TERM_GREEN, "\" to "); + p->write(TERM_L_GREEN, "inscribe"); + p->write(TERM_GREEN, " an item with \""); + p->write(TERM_WHITE, m_inscription.c_str()); + p->write(TERM_GREEN, "\" when"); + p->write(TERM_WHITE, "\n"); +} + +bool InscribeRule::do_apply_rule(object_type *o_ptr, int) const +{ + // Already inscribed? + if (o_ptr->note != 0) + { + return false; + } + + // Inscribe + msg_format("<Auto-Inscribe {%s}>", m_inscription.c_str()); + o_ptr->note = quark_add(m_inscription.c_str()); + return true; +} + +} // namespace |