#include "tome/squelch/condition_fwd.hpp" #include "tome/squelch/condition.hpp" #include #include "tome/squelch/cursor.hpp" #include "tome/squelch/tree_printer.hpp" #include "../ability_type.hpp" #include "../object1.hpp" #include "../object2.hpp" #include "../object_kind.hpp" #include "../object_type.hpp" #include "../player_race.hpp" #include "../player_race_mod.hpp" #include "../player_spec.hpp" #include "../player_type.hpp" #include "../skills.hpp" #include "../skill_type.hpp" #include "../util.hpp" #include "../variable.hpp" namespace squelch { EnumStringMap &match_mapping() { // TODO: This is quite ugly and leads to valgrind complaints static auto m = new EnumStringMap { { match_type::AND, "and" }, { match_type::OR, "or" }, { match_type::NOT, "not" }, { match_type::NAME, "name" }, { match_type::CONTAIN, "contain" }, { match_type::INSCRIBED, "inscribed" }, { match_type::DISCOUNT, "discount" }, { match_type::SYMBOL, "symbol" }, { match_type::STATE, "state" }, { match_type::STATUS, "status" }, { match_type::TVAL, "tval" }, { match_type::SVAL, "sval" }, { match_type::RACE, "race" }, { match_type::SUBRACE, "subrace" }, { match_type::CLASS, "class" }, { match_type::LEVEL, "level" }, { match_type::SKILL, "skill" }, { match_type::ABILITY, "ability" }, { match_type::INVENTORY, "inventory" }, { match_type::EQUIPMENT, "equipment" } }; return *m; }; EnumStringMap &identification_state_mapping() { // TODO: This is quite ugly and leads to valgrind complaints static auto m = new EnumStringMap { { identification_state::IDENTIFIED, "identified" }, { identification_state::NOT_IDENTIFIED, "not identified" } }; return *m; } jsoncons::json Condition::to_json() const { // Start with an object with only 'type' property jsoncons::json j; j["type"] = match_mapping().stringify(match); // Add sub-class properties to_json(j); // Return the completed JSON return j; } void Condition::display(TreePrinter *tree_printer, Cursor *cursor) const { assert(tree_printer); // Use normal or "selected" colours? uint8_t bcol = TERM_L_GREEN; uint8_t ecol = TERM_GREEN; if (cursor->is_selected(this)) { bcol = TERM_VIOLET; ecol = TERM_VIOLET; } // Indent a level and display tree. tree_printer->indent(); write_tree(tree_printer, cursor, ecol, bcol); tree_printer->dedent(); } std::shared_ptr Condition::parse_condition(jsoncons::json const &condition_json) { // Parsers for concrete types of conditions. static std::map< match_type, std::function(jsoncons::json const &)>> parsers { { match_type::AND, &AndCondition::from_json }, { match_type::OR, &OrCondition::from_json }, { match_type::NOT, &NotCondition::from_json }, { match_type::INVENTORY, &InventoryCondition::from_json }, { match_type::EQUIPMENT, &EquipmentCondition::from_json }, { match_type::NAME, &NameCondition::from_json }, { match_type::CONTAIN, &ContainCondition::from_json }, { match_type::SYMBOL, &SymbolCondition::from_json }, { match_type::INSCRIBED, &InscriptionCondition::from_json }, { match_type::DISCOUNT, &DiscountCondition::from_json }, { match_type::TVAL, &TvalCondition::from_json }, { match_type::SVAL, &SvalCondition::from_json }, { match_type::STATUS, &StatusCondition::from_json }, { match_type::STATE, &StateCondition::from_json }, { match_type::RACE, &RaceCondition::from_json }, { match_type::SUBRACE, &SubraceCondition::from_json }, { match_type::CLASS, &ClassCondition::from_json }, { match_type::LEVEL, &LevelCondition::from_json }, { match_type::SKILL, &SkillCondition::from_json }, { match_type::ABILITY, &AbilityCondition::from_json } }; if (condition_json.is_null()) { return nullptr; } cptr type_s = condition_json.get("type").as(); if (!type_s) { msg_print("Missing/invalid 'type' in condition"); return nullptr; } match_type match; if (!match_mapping().parse(type_s, &match)) { msg_format("Invalid 'type' in condition: %s", type_s); return nullptr; } // Look up parser and... parse auto parser_i = parsers.find(match); if (parser_i != parsers.end()) { return parser_i->second(condition_json); } assert(false && "Missing parser"); return nullptr; } jsoncons::json Condition::optional_to_json(std::shared_ptr condition) { return condition ? condition->to_json() : jsoncons::json::null_type(); } bool TvalCondition::is_match(object_type *o_ptr) const { return (o_ptr->tval == m_tval); } std::shared_ptr TvalCondition::from_json(jsoncons::json const &j) { auto tval_j = j.get("tval"); if (!tval_j.is_uinteger()) { msg_print("Missing/invalid 'tval' property"); return nullptr; } int tval = tval_j.as_uint(); return std::make_shared(tval); } void TvalCondition::to_json(jsoncons::json &j) const { j["tval"] = m_tval; } void TvalCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "tval"); p->write(ecol, " is "); p->write(ecol, "\""); p->write(TERM_WHITE, format("%d", (int) m_tval)); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } bool NameCondition::is_match(object_type *o_ptr) const { char buf1[128]; object_desc(buf1, o_ptr, -1, 0); return boost::algorithm::iequals(m_name, buf1); } std::shared_ptr NameCondition::from_json(jsoncons::json const &j) { cptr s = j.get("name").as(); if (!s) { msg_print("Missing/invalid 'name' property"); return nullptr; } return std::make_shared(s); } void NameCondition::write_tree(TreePrinter *p, Cursor *cursor, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "name"); p->write(ecol, " is \""); p->write(TERM_WHITE, m_name.c_str()); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void NameCondition::to_json(jsoncons::json &j) const { j["name"] = m_name; } bool ContainCondition::is_match(object_type *o_ptr) const { char buf1[128]; object_desc(buf1, o_ptr, -1, 0); return boost::algorithm::icontains(buf1, m_contain); } std::shared_ptr ContainCondition::from_json(jsoncons::json const &j) { cptr s = j.get("contain").as(); if (!s) { msg_print("Missing/invalid 'contain' property"); return nullptr; } return std::make_shared(s); } void ContainCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "name"); p->write(ecol, " contains \""); p->write(TERM_WHITE, m_contain.c_str()); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void ContainCondition::to_json(jsoncons::json &j) const { j["contain"] = m_contain; } bool SvalCondition::is_match(object_type *o_ptr) const { return (object_aware_p(o_ptr) && (o_ptr->sval >= m_min) && (o_ptr->sval <= m_max)); } std::shared_ptr SvalCondition::from_json(jsoncons::json const &j) { auto min_j = j.get("min"); if (!min_j.is_uinteger()) { msg_print("Missing/invalid 'min' property"); return nullptr; } auto max_j = j.get("max"); if (!max_j.is_uinteger()) { msg_print("Missing/invalid 'max' property"); return nullptr; } return std::make_shared( min_j.as_uint(), max_j.as_uint()); } void SvalCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "sval"); p->write(ecol, " is from "); p->write(TERM_WHITE, format("%d", m_min)); p->write(ecol, " to "); p->write(TERM_WHITE, format("%d", m_max)); p->write(TERM_WHITE, "\n"); } void SvalCondition::to_json(jsoncons::json &j) const { j["min"] = m_min; j["max"] = m_max; } void GroupingCondition::add_child(ConditionFactory const &factory) { auto c_ptr = factory(); if (c_ptr) { m_conditions.push_back(c_ptr); } } void GroupingCondition::remove_child(Condition *condition) { m_conditions.erase( std::remove_if( std::begin(m_conditions), std::end(m_conditions), [&] (std::shared_ptr p) { return p.get() == condition; }), std::end(m_conditions)); } std::shared_ptr GroupingCondition::first_child() { if (!m_conditions.empty()) { return m_conditions.front(); } return nullptr; } std::shared_ptr GroupingCondition::previous_child(Condition *current) { std::shared_ptr prev_condition; for (auto condition_p : m_conditions) { if (condition_p.get() == current) { // Do we have a previous child? if (prev_condition) { return prev_condition; } else { // No predecessor return nullptr; } } // Keep track of predecessor prev_condition = condition_p; } return nullptr; } std::shared_ptr GroupingCondition::next_child(Condition *current) { for (auto it = m_conditions.begin(); it != m_conditions.end(); it++) { if (it->get() == current) { it++; // Move to next child (if any) if (it == m_conditions.end()) { // No successor return nullptr; } return *it; } } return nullptr; } std::vector< std::shared_ptr > GroupingCondition::parse_conditions(jsoncons::json const &j) { auto conditions_j = j.get("conditions"); if (conditions_j.is_null()) { return std::vector< std::shared_ptr >(); } else if (!conditions_j.is_array()) { msg_print("'conditions' property has invalid type"); return std::vector< std::shared_ptr >(); } else { std::vector< std::shared_ptr > subconditions; for (auto const &subcondition_j: conditions_j.array_value()) { std::shared_ptr subcondition = parse_condition(subcondition_j); if (subcondition != nullptr) { subconditions.push_back(subcondition); } } return subconditions; } } void GroupingCondition::to_json(jsoncons::json &j) const { // Put all the sub-conditions into an array jsoncons::json::array ja; for (auto condition_p : m_conditions) { ja.push_back(optional_to_json(condition_p)); } // Add to JSON object j["conditions"] = ja; } bool AndCondition::is_match(object_type *o_ptr) const { for (auto condition_p : m_conditions) { if (!condition_p->is_match(o_ptr)) { return false; } } return true; } std::shared_ptr AndCondition::from_json(jsoncons::json const &j) { auto condition = std::make_shared(); for (auto subcondition : parse_conditions(j)) { condition->add_condition(subcondition); } return condition; } void AndCondition::write_tree(TreePrinter *p, Cursor *c, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "All of the following are true:"); p->write(TERM_WHITE, "\n"); for (auto condition_p : m_conditions) { condition_p->display(p, c); } } bool OrCondition::is_match(object_type *o_ptr) const { for (auto condition_p : m_conditions) { if (condition_p->is_match(o_ptr)) { return true; } } return false; } std::shared_ptr OrCondition::from_json(jsoncons::json const &j) { std::shared_ptr condition = std::make_shared(); for (auto subcondition : parse_conditions(j)) { condition->add_condition(subcondition); } return condition; } void OrCondition::write_tree(TreePrinter *p, Cursor *c, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "At least one of the following are true:"); p->write(TERM_WHITE, "\n"); for (auto condition_p : m_conditions) { condition_p->display(p, c); } } bool StatusCondition::is_match(object_type *o_ptr) const { return m_status == object_status(o_ptr); } std::shared_ptr StatusCondition::from_json(jsoncons::json const &j) { cptr s = j.get("status").as(); if (!s) { msg_print("Missing/invalid 'status' property"); return nullptr; } status_type status; if (!status_mapping().parse(s, &status)) { msg_format("Invalid 'status' property: %s", s); return nullptr; } return std::make_shared(status); } void StatusCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "status"); p->write(ecol, " is "); p->write(ecol, "\""); p->write(TERM_WHITE, status_mapping().stringify(m_status)); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void StatusCondition::to_json(jsoncons::json &j) const { j["status"] = status_mapping().stringify(m_status); } bool RaceCondition::is_match(object_type *o_ptr) const { return boost::algorithm::iequals(m_race, rp_ptr->title); } std::shared_ptr RaceCondition::from_json(jsoncons::json const &j) { cptr s = j.get("race").as(); if (!s) { msg_print("Missing/invalid 'race' property"); return nullptr; } return std::make_shared(s); } void RaceCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Player "); p->write(bcol, "race"); p->write(ecol, " is "); p->write(ecol, "\""); p->write(TERM_WHITE, m_race.c_str()); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void RaceCondition::to_json(jsoncons::json &j) const { j["race"] = m_race; } bool SubraceCondition::is_match(object_type *o_ptr) const { return boost::algorithm::iequals(m_subrace, rmp_ptr->title); } std::shared_ptr SubraceCondition::from_json(jsoncons::json const &j) { cptr s = j.get("subrace").as(); if (!s) { msg_print("Missing/invalid 'subrace' property"); return nullptr; } return std::make_shared(s); } void SubraceCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Player "); p->write(bcol, "subrace"); p->write(ecol, " is "); p->write(ecol, "\""); p->write(TERM_WHITE, m_subrace.c_str()); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void SubraceCondition::to_json(jsoncons::json &j) const { j["subrace"] = m_subrace; } bool ClassCondition::is_match(object_type *o_ptr) const { return boost::algorithm::iequals(m_class, spp_ptr->title); } std::shared_ptr ClassCondition::from_json(jsoncons::json const &j) { cptr s = j.get("class").as(); if (!s) { msg_print("Missing/invalid 'class' property"); return nullptr; } return std::make_shared(s); } void ClassCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Player "); p->write(bcol, "class"); p->write(ecol, " is "); p->write(ecol, "\""); p->write(TERM_WHITE, m_class.c_str()); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void ClassCondition::to_json(jsoncons::json &j) const { j["class"] = m_class; } bool InscriptionCondition::is_match(object_type *o_ptr) const { return boost::algorithm::icontains( o_ptr->inscription, m_inscription); } std::shared_ptr InscriptionCondition::from_json(jsoncons::json const &j) { cptr s = j.get("inscription").as(); if (!s) { msg_print("Missing/invalid 'inscription' property"); return nullptr; } return std::make_shared(s); } void InscriptionCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "It is "); p->write(bcol, "inscribed"); p->write(ecol, " with "); p->write(ecol, "\""); p->write(TERM_WHITE, m_inscription.c_str()); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void InscriptionCondition::to_json(jsoncons::json &j) const { j["inscription"] = m_inscription; } bool DiscountCondition::is_match(object_type *o_ptr) const { return (object_aware_p(o_ptr) && (o_ptr->discount >= m_min) && (o_ptr->discount <= m_max)); } std::shared_ptr DiscountCondition::from_json(jsoncons::json const &j) { auto min_j = j.get("min"); if (!min_j.is_integer()) { msg_print("Missing/invalid 'min' property"); return nullptr; } int min = min_j.as_int(); auto max_j = j.get("max"); if (!max_j.is_integer()) { msg_print("Missing/invalid 'max' property"); return nullptr; } int max = max_j.as_int(); return std::make_shared(min, max); } void DiscountCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "discount"); p->write(ecol, " is from "); p->write(TERM_WHITE, format("%d", m_min)); p->write(ecol, " to "); p->write(TERM_WHITE, format("%d", m_max)); p->write(TERM_WHITE, "\n"); } void DiscountCondition::to_json(jsoncons::json &j) const { j["min"] = m_min; j["max"] = m_max; } bool LevelCondition::is_match(object_type *) const { return ((p_ptr->lev >= m_min) && (p_ptr->lev <= m_max)); } std::shared_ptr LevelCondition::from_json(jsoncons::json const &j) { auto min_j = j.get("min"); if (!min_j.is_integer()) { msg_print("Missing/invalid 'min' property"); return nullptr; } int min = min_j.as_int(); auto max_j = j.get("max"); if (!max_j.is_integer()) { msg_print("Missing/invalid 'max' property"); return nullptr; } int max = max_j.as_int(); return std::make_shared(min, max); } void LevelCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Your "); p->write(bcol, "level"); p->write(ecol, " is from "); p->write(TERM_WHITE, format("%d", m_min)); p->write(ecol, " to "); p->write(TERM_WHITE, format("%d", m_max)); p->write(TERM_WHITE, "\n"); } void LevelCondition::to_json(jsoncons::json &j) const { j["min"] = m_min; j["max"] = m_max; } bool SkillCondition::is_match(object_type *) const { uint16_t sk = get_skill(m_skill_idx); return ((sk >= m_min) && (sk <= m_max)); } std::shared_ptr SkillCondition::from_json(jsoncons::json const &j) { auto min_j = j.get("min"); if (!min_j.is_integer()) { msg_print("Missing/invalid 'min' property"); return nullptr; } int min = min_j.as_int(); auto max_j = j.get("max"); if (!max_j.is_integer()) { msg_print("Missing/invalid 'max' property"); return nullptr; } int max = max_j.as_int(); auto s = j.get("name").as(); if (!s) { msg_print("Missing/invalid 'name' property"); return nullptr; } auto si = find_skill_i(s); if (si < 0) { msg_print("Invalid 'name' property"); return nullptr; } return std::make_shared(si, min, max); } void SkillCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Your skill in "); p->write(bcol, s_descriptors[m_skill_idx].name); p->write(ecol, " is from "); p->write(TERM_WHITE, format("%d", (int) m_min)); p->write(ecol, " to "); p->write(TERM_WHITE, format("%d", (int) m_max)); p->write(TERM_WHITE, "\n"); } void SkillCondition::to_json(jsoncons::json &j) const { j["name"] = s_descriptors[m_skill_idx].name; j["min"] = m_min; j["max"] = m_max; } bool StateCondition::is_match(object_type *o_ptr) const { switch (m_state) { case identification_state::IDENTIFIED: return object_known_p(o_ptr); case identification_state::NOT_IDENTIFIED: return !object_known_p(o_ptr); } assert(false); return false; } std::shared_ptr StateCondition::from_json(jsoncons::json const &j) { cptr s = j.get("state").as(); if (!s) { msg_print("Missing/invalid 'state' property"); return nullptr; } identification_state state; if (!identification_state_mapping().parse(s, &state)) { msg_format("Invalid 'state' property: %s", s); return nullptr; } return std::make_shared(state); } void StateCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "state"); p->write(ecol, " is "); p->write(ecol, "\""); p->write(TERM_WHITE, identification_state_mapping().stringify(m_state)); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void StateCondition::to_json(jsoncons::json &j) const { j["state"] = identification_state_mapping().stringify(m_state); } bool SymbolCondition::is_match(object_type *o_ptr) const { object_kind *k_ptr = &k_info[o_ptr->k_idx]; return k_ptr->d_char == m_symbol; } std::shared_ptr SymbolCondition::from_json(jsoncons::json const &j) { auto s = j.get("symbol").as(); if (s.empty()) { msg_print("Missing/invalid 'symbol' property"); return nullptr; } if (s.size() > 1) { msg_print("Invalid 'symbol' property: Too long"); return nullptr; } return std::make_shared(s[0]); } void SymbolCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { p->write(ecol, "Its "); p->write(bcol, "symbol"); p->write(ecol, " is "); p->write(ecol, "\""); p->write(TERM_WHITE, format("%c", m_symbol)); p->write(ecol, "\""); p->write(TERM_WHITE, "\n"); } void SymbolCondition::to_json(jsoncons::json &j) const { j["symbol"] = format("%c", m_symbol); } bool AbilityCondition::is_match(object_type *) const { return p_ptr->has_ability(m_ability_idx); } std::shared_ptr AbilityCondition::from_json(jsoncons::json const &j) { cptr a = j.get("ability").as(); if (!a) { msg_print("Missing/invalid 'ability' property"); return nullptr; } auto ai = find_ability(a); if (ai < 0) { msg_print("Invalid 'ability' property"); return nullptr; } return std::make_shared(ai); } void AbilityCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const { cptr ability_s = ab_info[m_ability_idx].name; p->write(ecol, "You have the "); p->write(bcol, ability_s); p->write(ecol, " ability"); p->write(TERM_WHITE, "\n"); } void AbilityCondition::to_json(jsoncons::json &j) const { j["ability"] = ab_info[m_ability_idx].name; } void SingleSubconditionCondition::add_child(std::function< std::shared_ptr () > const &factory) { // If we already have a subcondition then we cannot // add one. if (!m_subcondition) { m_subcondition = factory(); } } void SingleSubconditionCondition::remove_child(Condition *c) { if (m_subcondition.get() == c) { m_subcondition.reset(); } } std::shared_ptr SingleSubconditionCondition::first_child() { return m_subcondition; } void SingleSubconditionCondition::to_json(jsoncons::json &j) const { j["condition"] = optional_to_json(m_subcondition); } std::shared_ptr SingleSubconditionCondition::parse_single_subcondition(jsoncons::json const &in_json) { auto condition_j = in_json.get("condition"); if (condition_j.is_null()) { return nullptr; } else if (!condition_j.is_object()) { msg_format("Invalid 'condition' property"); return nullptr; } else { return parse_condition(condition_j); } } bool NotCondition::is_match(object_type *o_ptr) const { if (!m_subcondition) { return true; } return !m_subcondition->is_match(o_ptr); } std::shared_ptr NotCondition::from_json(jsoncons::json const &j) { return std::make_shared(parse_single_subcondition(j)); } void NotCondition::write_tree(TreePrinter *p, Cursor *c, byte ecol, byte bcol) const { p->write(ecol, "Negate the following:"); p->write(TERM_WHITE, "\n"); if (m_subcondition) { m_subcondition->display(p, c); } } bool InventoryCondition::is_match(object_type *) const { if (!m_subcondition) { return false; } for (int i = 0; i < INVEN_WIELD; i++) { if (m_subcondition->is_match(&p_ptr->inventory[i])) { return true; } } return false; } std::shared_ptr InventoryCondition::from_json(jsoncons::json const &j) { return std::make_shared( parse_single_subcondition(j)); } void InventoryCondition::write_tree(TreePrinter *p, Cursor *c, byte ecol, byte bcol) const { p->write(ecol, "Something in your "); p->write(bcol, "inventory"); p->write(ecol, " matches the following:"); p->write(TERM_WHITE, "\n"); if (m_subcondition) { m_subcondition->display(p, c); } } bool EquipmentCondition::is_match(object_type *) const { if (!m_subcondition) { return false; } for (int i = INVEN_WIELD; i < INVEN_TOTAL; i++) { if (m_subcondition->is_match(&p_ptr->inventory[i])) { return true; } } return false; } std::shared_ptr EquipmentCondition::from_json(jsoncons::json const &j) { return std::make_shared( parse_single_subcondition(j)); } void EquipmentCondition::write_tree(TreePrinter *p, Cursor *c, byte ecol, byte bcol) const { p->write(ecol, "Something in your "); p->write(bcol, "equipment"); p->write(ecol, " matches the following:"); p->write(TERM_WHITE, "\n"); if (m_subcondition) { m_subcondition->display(p, c); } } } // namespace