static std::shared_ptr<unit_filter_abstract_impl> construct(const vconfig & vcfg, const filter_context & fc, bool flat_tod) { if (vcfg.empty()) { return std::make_shared<null_unit_filter_impl> (fc); } if (vcfg.get_config().attribute_count() == 1 && vcfg.get_config().all_children_count() == 0 && vcfg.has_attribute("limit")) { return std::make_shared<null_unit_filter_impl> (fc); } return std::make_shared<basic_unit_filter_impl>(vcfg, fc, flat_tod); //TODO: Add more efficient implementations for special cases }
/** * Updates *this based on @a vcfg. * This corresponds to what can appear in [set_menu_item]. */ void wml_menu_item::update(const vconfig & vcfg) { const bool old_use_hotkey = use_hotkey_; // Tracks whether or not the hotkey has been updated. bool hotkey_updated = false; if ( vcfg.has_attribute("image") ) image_ = vcfg["image"].str(); if ( vcfg.has_attribute("description") ) { description_ = vcfg["description"].t_str(); hotkey_updated = true; } if ( vcfg.has_attribute("needs_select") ) needs_select_ = vcfg["needs_select"].to_bool(); if ( const vconfig & child = vcfg.child("show_if") ) { show_if_ = child; show_if_.make_safe(); } if ( const vconfig & child = vcfg.child("filter_location") ) { filter_location_ = child; filter_location_.make_safe(); } if ( const vconfig & child = vcfg.child("default_hotkey") ) { default_hotkey_ = child.get_parsed_config(); hotkey_updated = true; } if ( vcfg.has_attribute("use_hotkey") ) { const config::attribute_value & use_hotkey_av = vcfg["use_hotkey"]; use_hotkey_ = use_hotkey_av.to_bool(true); use_wml_menu_ = use_hotkey_av.str() != "only"; } if ( const vconfig & cmd = vcfg.child("command") ) { const bool delayed = cmd["delayed_variable_substitution"].to_bool(true); update_command(delayed ? cmd.get_config() : cmd.get_parsed_config()); } // Update the registered hotkey? if ( use_hotkey_ && !old_use_hotkey ) // The hotkey needs to be enabled. hotkey::add_wml_hotkey(hotkey_id_, description_, default_hotkey_); else if ( use_hotkey_ && hotkey_updated ) // The hotkey needs to be updated. hotkey::add_wml_hotkey(hotkey_id_, description_, default_hotkey_); else if ( !use_hotkey_ && old_use_hotkey ) // The hotkey needs to be disabled. hotkey::remove_wml_hotkey(hotkey_id_); }
void handle_event_command(const std::string &cmd, const queued_event &event_info, const vconfig &cfg) { log_scope2(log_engine, "handle_event_command"); LOG_NG << "handling command '" << cmd << "' from " << (cfg.is_volatile()?"volatile ":"") << "cfg 0x" << std::hex << std::setiosflags(std::ios::uppercase) << reinterpret_cast<uintptr_t>(&cfg.get_config()) << std::dec << "\n"; if (!resources::lua_kernel->run_wml_action(cmd, cfg, event_info)) { ERR_NG << "Couldn't find function for wml tag: "<< cmd <<"\n"; } DBG_NG << "done handling command...\n"; }
/** * Implements the lifting and resetting of fog via WML. * Keeping affect_normal_fog as false causes only the fog override to be affected. * Otherwise, fog lifting will be implemented similar to normal sight (cannot be * individually reset and ends at the end of the turn), and fog resetting will, in * addition to removing overrides, extend the specified teams' normal fog to all * hexes. */ void toggle_fog(const bool clear, const vconfig& cfg, const bool affect_normal_fog=false) { // Filter the sides. const vconfig &ssf = cfg.child("filter_side"); const side_filter s_filter(ssf.null() ? vconfig::empty_vconfig() : ssf, resources::filter_con); const std::vector<int> sides = s_filter.get_teams(); // Filter the locations. std::set<map_location> locs; const terrain_filter t_filter(cfg, resources::filter_con); t_filter.get_locations(locs, true); // Loop through sides. BOOST_FOREACH(const int &side_num, sides) { team &t = (*resources::teams)[side_num-1]; if ( !clear ) { // Extend fog. t.remove_fog_override(locs); if ( affect_normal_fog ) t.refog(); } else if ( !affect_normal_fog ) // Force the locations clear of fog. t.add_fog_override(locs); else // Simply clear fog from the locations. BOOST_FOREACH(const map_location &hex, locs) t.clear_fog(hex); }
void verify_and_set_global_variable(const vconfig &pcfg) { bool valid = true; if (!pcfg.has_attribute("to_global")) { LOG_PERSIST << "Error: [set_global_variable] missing required attribute \"from_global\""; valid = false; } if (!pcfg.has_attribute("from_local")) { LOG_PERSIST << "Warning: [set_global_variable] missing attribute \"to_local\", global variable will be cleared"; } // TODO: allow for global namespace. if (!pcfg.has_attribute("namespace")) { LOG_PERSIST << "Error: [set_global_variable] missing attribute \"namespace\" and no global namespace provided."; valid = false; } if (network::nconnections() != 0) { if (!pcfg.has_attribute("side")) { LOG_PERSIST << "Error: [set_global_variable] missing attribute \"side\" required in multiplayer context."; valid = false; } else { config::attribute_value pcfg_side = pcfg["side"]; int side = pcfg_side; //Check side matching only if the side is not "global". if (pcfg_side.str() != "global") { //Ensure that the side is valid. if (unsigned(side-1) > resources::teams->size()) { LOG_PERSIST << "Error: [set_global_variable] attribute \"side\" specifies invalid side number."; valid = false; } else { //Set the variable only if it is meant for a side we control valid = (*resources::teams)[side - 1].is_local(); } } } } if (valid) { persist_context &ctx = resources::persist->get_context((pcfg["namespace"])); if (ctx.valid()) { set_global_variable(ctx,pcfg); } else { LOG_PERSIST << "Error: [set_global_variable] attribute \"namespace\" is not valid."; } } }
void verify_and_get_global_variable(const vconfig &pcfg) { bool valid = true; if (!pcfg.has_attribute("from_global")) { LOG_PERSIST << "Error: [get_global_variable] missing required attribute \"from_global\""; valid = false; } if (!pcfg.has_attribute("to_local")) { LOG_PERSIST << "Error: [get_global_variable] missing required attribute \"to_local\""; valid = false; } // TODO: allow for global namespace. if (!pcfg.has_attribute("namespace")) { LOG_PERSIST << "Error: [get_global_variable] missing attribute \"namespace\" and no global namespace provided."; valid = false; } if (network::nconnections() != 0) { if (!pcfg.has_attribute("side")) { LOG_PERSIST << "Error: [get_global_variable] missing attribute \"side\" required in multiplayer context."; valid = false; } else { DBG_PERSIST << "verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"] << "\n"; config::attribute_value pcfg_side = pcfg["side"]; int side = pcfg_side.str() == "global" ? resources::controller->current_side() : pcfg_side.to_int(); if (unsigned (side - 1) >= resources::teams->size()) { LOG_PERSIST << "Error: [get_global_variable] attribute \"side\" specifies invalid side number." << "\n"; valid = false; } else { } DBG_PERSIST << "end verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"] << "\n"; } } if (valid) { persist_context &ctx = resources::persist->get_context((pcfg["namespace"])); if (ctx.valid()) { get_global_variable(ctx,pcfg); } else { LOG_PERSIST << "Error: [get_global_variable] attribute \"namespace\" is not valid."; } } }
bool have_location(const vconfig& cfg) { std::set<map_location> res; terrain_filter(cfg, resources::filter_con).get_locations(res); std::vector<std::pair<int,int> > counts = cfg.has_attribute("count") ? utils::parse_ranges(cfg["count"]) : default_counts; return in_ranges<int>(res.size(), counts); }
void verify_and_set_global_variable(const vconfig &pcfg) { bool valid = true; if (!pcfg.has_attribute("to_global")) { ERR_PERSIST << "[set_global_variable] missing required attribute \"to_global\""; valid = false; } if (!pcfg.has_attribute("from_local")) { LOG_PERSIST << "Warning: [set_global_variable] missing attribute \"from_local\", global variable will be cleared"; } // TODO: allow for global namespace. if (!pcfg.has_attribute("namespace")) { ERR_PERSIST << "[set_global_variable] missing attribute \"namespace\" and no global namespace provided."; valid = false; } if (resources::controller->is_networked_mp()) { config::attribute_value pcfg_side = pcfg["side"]; int side = pcfg_side; //Check side matching only if the side is not "global" or empty. if (pcfg_side.str() != "global" && !pcfg_side.empty()) { //Ensure that the side is valid. if (unsigned(side-1) > resources::gameboard->teams().size()) { ERR_PERSIST << "[set_global_variable] attribute \"side\" specifies invalid side number."; valid = false; } else if (resources::gameboard->teams()[side - 1].is_empty()) { LOG_PERSIST << "[set_global_variable] attribute \"side\" specifies a null-controlled side number."; valid = false; } else { //Set the variable only if it is meant for a side we control valid = resources::gameboard->teams()[side - 1].is_local(); } } } if (valid) { persist_context &ctx = resources::persist->get_context((pcfg["namespace"])); if (ctx.valid()) { set_global_variable(ctx,pcfg); } else { LOG_PERSIST << "Error: [set_global_variable] attribute \"namespace\" is not valid."; } } }
bool matches_special_filter(const config &cfg, const vconfig& filter) { if (!cfg) { WRN_NG << "attempt to filter attack for an event with no attack data." << std::endl; // better to not execute the event (so the problem is more obvious) return false; } const attack_type attack(cfg); return attack.matches_filter(filter.get_parsed_config()); }
basic_unit_filter_impl(const vconfig & vcfg, const filter_context & fc, bool flat_tod) : fc_(fc) , vcfg(vcfg) , use_flat_tod_(flat_tod) , cond_children_() , cond_child_types_() { // Handle [and], [or], and [not] with in-order precedence vconfig::all_children_iterator cond = vcfg.ordered_begin(); vconfig::all_children_iterator cond_end = vcfg.ordered_end(); while(cond != cond_end) { const std::string& cond_name = cond.get_key(); conditional::TYPE type; if(type.parse(cond_name)) { const vconfig& cond_filter = cond.get_child(); cond_children_.push_back(unit_filter(cond_filter, &fc_, use_flat_tod_)); cond_child_types_.push_back(type); } else { static const int NUM_VALID_TAGS = 5; static const std::string valid_tags[NUM_VALID_TAGS] = { "filter_vision", "filter_adjacent", "filter_location", "filter_side", "filter_wml", }; static const std::string* const valid_tags_end = valid_tags + NUM_VALID_TAGS; if (std::find(valid_tags, valid_tags_end, cond_name) == valid_tags_end){ std::stringstream errmsg; errmsg << "encountered a child [" << cond_name << "] of a standard unit filter, it is being ignored"; DBG_CF << errmsg.str() << std::endl; //FAIL( errmsg.str() ); } } ++cond; } this->vcfg.make_safe(); }
pathfind::teleport_group::teleport_group(const vconfig& cfg, bool reversed) : cfg_(cfg.get_config()), reversed_(reversed), id_() { assert(cfg_.child_count("source") == 1); assert(cfg_.child_count("target") == 1); assert(cfg_.child_count("filter") == 1); if (cfg["id"].empty()) { id_ = resources::tunnels->next_unique_id(); } else { id_ = cfg["id"].str(); if (reversed_) // Differentiate the reverse tunnel from the forward one id_ += reversed_suffix; } }
void verify_and_clear_global_variable(const vconfig &pcfg) { bool valid = true; if (!pcfg.has_attribute("global")) { ERR_PERSIST << "[clear_global_variable] missing required attribute \"from_global\""; valid = false; } if (!pcfg.has_attribute("namespace")) { ERR_PERSIST << "[clear_global_variable] missing attribute \"namespace\" and no global namespace provided."; valid = false; } if (network::nconnections() != 0) { config::attribute_value pcfg_side = pcfg["side"]; const int side = pcfg_side.to_int(); //Check side matching only if the side is not "global" or empty. if (pcfg_side.str() != "global" && !pcfg_side.empty()) { //Ensure that the side is valid. if (unsigned(side-1) > resources::teams->size()) { ERR_PERSIST << "[clear_global_variable] attribute \"side\" specifies invalid side number."; valid = false; } else if ((*resources::teams)[side - 1].is_empty()) { LOG_PERSIST << "[clear_global_variable] attribute \"side\" specifies a null-controlled side number."; valid = false; } else { //Clear the variable only if it is meant for a side we control valid = (*resources::teams)[side - 1].is_local(); } } } if (valid) { persist_context &ctx = resources::persist->get_context((pcfg["namespace"])); if (ctx.valid()) { clear_global_variable(ctx,pcfg); } else { LOG_PERSIST << "Error: [clear_global_variable] attribute \"namespace\" is not valid."; } } }
void verify_and_get_global_variable(const vconfig &pcfg) { bool valid = true; if (!pcfg.has_attribute("from_global")) { ERR_PERSIST << "[get_global_variable] missing required attribute \"from_global\""; valid = false; } if (!pcfg.has_attribute("to_local")) { ERR_PERSIST << "[get_global_variable] missing required attribute \"to_local\""; valid = false; } // TODO: allow for global namespace. if (!pcfg.has_attribute("namespace")) { ERR_PERSIST << "[get_global_variable] missing attribute \"namespace\""; valid = false; } if (resources::controller->is_networked_mp()) { DBG_PERSIST << "verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"] << "\n"; config::attribute_value pcfg_side = pcfg["side"]; int side = (pcfg_side.str() == "global" || pcfg_side.empty()) ? resources::controller->current_side() : pcfg_side.to_int(); if (unsigned (side - 1) >= resources::gameboard->teams().size()) { ERR_PERSIST << "[get_global_variable] attribute \"side\" specifies invalid side number." << "\n"; valid = false; } DBG_PERSIST << "end verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"] << "\n"; } if (valid) { persist_context &ctx = resources::persist->get_context((pcfg["namespace"])); if (ctx.valid()) { get_global_variable(ctx,pcfg); } else { LOG_PERSIST << "Error: [get_global_variable] attribute \"namespace\" is not valid."; } } }
/** * Determines if @a un_it matches @a filter. If the filter is not empty, * the unit is required to additionally match the unit that was supplied * when this was constructed. */ bool entity_location::matches_unit_filter(const unit_map::const_iterator & un_it, const vconfig & filter) const { if ( !un_it.valid() ) return false; if ( filter.empty() ) // Skip the check for un_it matching *this. return true; // Filter the unit at the filter location (should be the unit's // location if no special filter location was specified). return unit_filter(filter).matches(*un_it, filter_loc_) && matches_unit(un_it); }
bool have_unit(const vconfig& cfg) { if(resources::units == nullptr) { return false; } std::vector<std::pair<int,int> > counts = cfg.has_attribute("count") ? utils::parse_ranges(cfg["count"]) : default_counts; int match_count = 0; const unit_filter ufilt(cfg, resources::filter_con); for(const unit &i : *resources::units) { if(i.hitpoints() > 0 && ufilt(i)) { ++match_count; if(counts == default_counts) { // by default a single match is enough, so avoid extra work break; } } } if(cfg["search_recall_list"].to_bool()) { for(const team& team : resources::gameboard->teams()) { if(counts == default_counts && match_count) { break; } for(size_t t = 0; t < team.recall_list().size(); ++t) { if(counts == default_counts && match_count) { break; } scoped_recall_unit auto_store("this_unit", team.save_id(), t); if(ufilt(*team.recall_list()[t])) { ++match_count; } } } } return in_ranges(match_count, counts); }
void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location) { unit_const_ptr u; unit_map::const_iterator u_it = resources::units->find(default_location); if (u_it.valid()) { u = u_it.get_shared_ptr(); } // Search for a valid unit filter, // and if we have one, look for the matching unit vconfig filter = cfg.child("filter"); if(!filter.null()) { const unit_filter ufilt(filter, resources::filter_con); u = ufilt.first_match_on_map(); } // We have found a unit that matches the filter if (u && !resources::screen->fogged(u->get_location())) { attack_type *primary = nullptr; attack_type *secondary = nullptr; Uint32 text_color; unit_animation::hit_type hits= unit_animation::hit_type::INVALID; std::vector<attack_type> attacks = u->attacks(); std::vector<attack_type>::iterator itor; std::unique_ptr<attack_type> dummy_primary; std::unique_ptr<attack_type> dummy_secondary; // death and victory animations are handled here because usually // the code iterates through all the unit's attacks // but in these two specific cases we need to create dummy attacks // to fire correctly certain animations // this is especially evident with the Wose's death animations if (cfg["flag"] == "death" || cfg["flag"] == "victory") { filter = cfg.child("primary_attack"); if(!filter.null()) { dummy_primary.reset(new attack_type(filter.get_config())); primary = dummy_primary.get(); } filter = cfg.child("secondary_attack"); if(!filter.null()) { dummy_secondary.reset(new attack_type(filter.get_config())); secondary = dummy_secondary.get(); } } else { filter = cfg.child("primary_attack"); if(!filter.null()) { for(itor = attacks.begin(); itor != attacks.end(); ++itor){ if(itor->matches_filter(filter.get_parsed_config())) { primary = &*itor; break; } } } filter = cfg.child("secondary_attack"); if(!filter.null()) { for(itor = attacks.begin(); itor != attacks.end(); ++itor){ if(itor->matches_filter(filter.get_parsed_config())) { secondary = &*itor; break; } } } } if(cfg["hits"] == "yes" || cfg["hits"] == "hit") { hits = unit_animation::hit_type::HIT; } if(cfg["hits"] == "no" || cfg["hits"] == "miss") { hits = unit_animation::hit_type::MISS; } if( cfg["hits"] == "kill" ) { hits = unit_animation::hit_type::KILL; } if(cfg["red"].empty() && cfg["green"].empty() && cfg["blue"].empty()) { text_color = display::rgb(0xff,0xff,0xff); } else { text_color = display::rgb(cfg["red"], cfg["green"], cfg["blue"]); } resources::screen->scroll_to_tile(u->get_location(), game_display::ONSCREEN, true, false); vconfig t_filter = cfg.child("facing"); map_location secondary_loc = map_location::null_location(); if(!t_filter.empty()) { terrain_filter filter(t_filter, resources::filter_con); std::set<map_location> locs; filter.get_locations(locs); if (!locs.empty() && u->get_location() != *locs.begin()) { map_location::DIRECTION dir =u->get_location().get_relative_dir(*locs.begin()); u->set_facing(dir); secondary_loc = u->get_location().get_direction(dir); } } config::attribute_value text = u->gender() == unit_race::FEMALE ? cfg["female_text"] : cfg["male_text"]; if(text.blank()) { text = cfg["text"]; } animator.add_animation(&*u, cfg["flag"], u->get_location(), secondary_loc, cfg["value"], cfg["with_bars"].to_bool(), text.str(), text_color, hits, primary, secondary, cfg["value_second"]); } const vconfig::child_list sub_anims = cfg.get_children("animate"); vconfig::child_list::const_iterator anim_itor; for(anim_itor = sub_anims.begin(); anim_itor != sub_anims.end();++anim_itor) { wml_animation_internal(animator, *anim_itor); } }
void unit_filter_compound::fill(vconfig cfg) { const config& literal = cfg.get_config(); //optimisation if(literal.empty()) { return; } create_attribute(literal["name"], [](const config::attribute_value& c) { return c.t_str(); }, [](const t_string& str, const unit_filter_args& args) { return str == args.u.name(); } ); create_attribute(literal["id"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& id_list, const unit_filter_args& args) { return std::find(id_list.begin(), id_list.end(), args.u.id()) != id_list.end(); } ); create_attribute(literal["speaker"], [](const config::attribute_value& c) { return c.str(); }, [](const std::string& speaker, const unit_filter_args& args) { return speaker == args.u.id(); } ); create_attribute(literal["type"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& types, const unit_filter_args& args) { return std::find(types.begin(), types.end(), args.u.type_id()) != types.end(); } ); create_attribute(literal["type_adv_tree"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& types, const unit_filter_args& args) { std::set<std::string> types_expanded; for(const std::string& type : types) { if(types_expanded.count(type)) { continue; } if(const unit_type* ut = unit_types.find(type)) { const auto& tree = ut->advancement_tree(); types_expanded.insert(tree.begin(), tree.end()); types_expanded.insert(type); } } return types_expanded.find(args.u.type_id()) != types_expanded.end(); } ); create_attribute(literal["variation"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& types, const unit_filter_args& args) { return std::find(types.begin(), types.end(), args.u.variation()) != types.end(); } ); create_attribute(literal["has_variation"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& types, const unit_filter_args& args) { // If this unit is a variation itself then search in the base unit's variations. const unit_type* const type = args.u.variation().empty() ? &args.u.type() : unit_types.find(args.u.type().base_id()); assert(type); for(const std::string& variation_id : types) { if (type->has_variation(variation_id)) { return true; } } return false; } ); create_attribute(literal["ability"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& abilities, const unit_filter_args& args) { for(const std::string& ability_id : abilities) { if (args.u.has_ability_by_id(ability_id)) { return true; } } return false; } ); create_attribute(literal["ability_type"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& abilities, const unit_filter_args& args) { for(const std::string& ability : abilities) { if (args.u.has_ability_type(ability)) { return true; } } return false; } ); create_attribute(literal["ability_type_active"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& abilities, const unit_filter_args& args) { for(const std::string& ability : abilities) { if (!args.u.get_abilities(ability, args.loc).empty()) { return true; } } return false; } ); create_attribute(literal["trait"], [](const config::attribute_value& c) { auto res = utils::split(c.str()); std::sort(res.begin(), res.end()); return res; }, [](const std::vector<std::string>& check_traits, const unit_filter_args& args) { std::vector<std::string> have_traits = args.u.get_traits_list(); std::vector<std::string> isect; std::sort(have_traits.begin(), have_traits.end()); std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect)); return !isect.empty(); } ); create_attribute(literal["race"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& races, const unit_filter_args& args) { return std::find(races.begin(), races.end(), args.u.race()->id()) != races.end(); } ); create_attribute(literal["gender"], [](const config::attribute_value& c) { return string_gender(c.str()); }, [](unit_race::GENDER gender, const unit_filter_args& args) { return gender == args.u.gender(); } ); create_attribute(literal["side"], [](const config::attribute_value& c) { std::vector<int> res; for(const std::string& s : utils::split(c.str())) { res.push_back(std::stoi(s)); } return res; }, [](const std::vector<int>& sides, const unit_filter_args& args) { return std::find(sides.begin(), sides.end(), args.u.side()) != sides.end(); } ); create_attribute(literal["status"], [](const config::attribute_value& c) { return utils::split(c.str()); }, [](const std::vector<std::string>& statuses, const unit_filter_args& args) { for(const std::string& status : statuses) { if (args.u.get_state(status)) { return true; } } return false; } ); create_attribute(literal["has_weapon"], [](const config::attribute_value& c) { return c.str(); }, [](const std::string& weapon, const unit_filter_args& args) { for(const attack_type& a : args.u.attacks()) { if(a.id() == weapon) { return true; } } return false; } ); create_attribute(literal["role"], [](const config::attribute_value& c) { return c.str(); }, [](const std::string& role, const unit_filter_args& args) { return args.u.get_role() == role; } ); create_attribute(literal["ai_special"], [](const config::attribute_value& c) { return c.str(); }, [](const std::string& ai_special, const unit_filter_args& args) { return (ai_special == "guardian") == args.u.get_state(unit::STATE_GUARDIAN); } ); create_attribute(literal["canrecruit"], [](const config::attribute_value& c) { return c.to_bool(); }, [](bool canrecruit, const unit_filter_args& args) { return args.u.can_recruit() == canrecruit; } ); create_attribute(literal["recall_cost"], [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args) { for(auto cost : ranges) { if(cost.first <= args.u.recall_cost() && args.u.recall_cost() <= cost.second) { return true; } } return false; } ); create_attribute(literal["level"], [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args) { for(auto lvl : ranges) { if(lvl.first <= args.u.level() && args.u.level() <= lvl.second) { return true; } } return false; } ); create_attribute(literal["defense"], [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args) { int actual_defense = args.u.defense_modifier(args.fc->get_disp_context().map().get_terrain(args.loc)); for(auto def : ranges) { if(def.first <= actual_defense && actual_defense <= def.second) { return true; } } return false; } ); create_attribute(literal["movement_cost"], [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args) { int actual_cost = args.u.movement_cost(args.fc->get_disp_context().map().get_terrain(args.loc)); for(auto cost : ranges) { if(cost.first <= actual_cost && actual_cost <= cost.second) { return true; } } return false; } ); create_attribute(literal["vision_cost"], [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args) { int actual_cost = args.u.vision_cost(args.fc->get_disp_context().map().get_terrain(args.loc)); for(auto cost : ranges) { if(cost.first <= actual_cost && actual_cost <= cost.second) { return true; } } return false; } ); create_attribute(literal["jamming_cost"], [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); }, [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args) { int actual_cost = args.u.jamming_cost(args.fc->get_disp_context().map().get_terrain(args.loc)); for(auto cost : ranges) { if(cost.first <= actual_cost && actual_cost <= cost.second) { return true; } } return false; } ); create_attribute(literal["lua_function"], [](const config::attribute_value& c) { return c.str(); }, [](const std::string& lua_function, const unit_filter_args& args) { if (game_lua_kernel * lk = args.fc->get_lua_kernel()) { return lk->run_filter(lua_function.c_str(), args.u); } return true; } ); create_attribute(literal["formula"], [](const config::attribute_value& c) { //TODO: catch syntax error. return wfl::formula(c, new wfl::gamestate_function_symbol_table()); }, [](const wfl::formula& form, const unit_filter_args& args) { try { const wfl::unit_callable main(args.loc, args.u); wfl::map_formula_callable callable(main.fake_ptr()); if (args.u2) { std::shared_ptr<wfl::unit_callable> secondary(new wfl::unit_callable(*args.u2)); callable.add("other", wfl::variant(secondary)); // It's not destroyed upon scope exit because the variant holds a reference } if(!form.evaluate(callable).as_bool()) { return false; } return true; } catch(wfl::formula_error& e) { lg::wml_error() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n"; // Formulae with syntax errors match nothing return false; } } ); create_attribute(literal["find_in"], [](const config::attribute_value& c) { return c.str(); }, [](const std::string& find_in, const unit_filter_args& args) { // Allow filtering by searching a stored variable of units if (const game_data * gd = args.fc->get_game_data()) { try { for (const config& c : gd->get_variable_access_read(find_in).as_array()) { if(c["id"] == args.u.id()) { return true; } } return false; } catch(const invalid_variablename_exception&) { return false; } } return true; } ); if (!literal["x"].blank() || !literal["y"].blank()) { children_.emplace_back(new unit_filter_xy(literal["x"], literal["y"])); } for(auto child : cfg.all_ordered()) { CONDITIONAL_TYPE cond; if(cond.parse(child.first)) { cond_children_.emplace_back(std::piecewise_construct_t(), std::make_tuple(cond), std::make_tuple(child.second)); } else if (child.first == "filter_wml") { create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { config fwml = c.get_parsed_config(); /* Check if the filter only cares about variables. If so, no need to serialize the whole unit. */ config::all_children_itors ci = fwml.all_children_range(); if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") { return args.u.variables().matches(ci.front().cfg); } else { config ucfg; args.u.write(ucfg); return ucfg.matches(fwml); } }); } else if (child.first == "filter_vision") { create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { std::set<int> viewers; side_filter ssf(c, args.fc); std::vector<int> sides = ssf.get_teams(); viewers.insert(sides.begin(), sides.end()); for (const int viewer : viewers) { bool fogged = args.fc->get_disp_context().get_team(viewer).fogged(args.loc); bool hiding = args.u.invisible(args.loc, args.fc->get_disp_context()) && args.fc->get_disp_context().get_team(viewer).is_enemy(args.u.side()); bool unit_hidden = fogged || hiding; if (c["visible"].to_bool(true) != unit_hidden) { return true; } } return false; }); } else if (child.first == "filter_adjacent") { children_.emplace_back(new unit_filter_adjacent(child.second)); } else if (child.first == "filter_location") { create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { return terrain_filter(c, args.fc, args.use_flat_tod).match(args.loc); }); } else if (child.first == "filter_side") { create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { return side_filter(c, args.fc).match(args.u.side()); }); } else if (child.first == "has_attack") { create_child(child.second, [](const vconfig& c, const unit_filter_args& args) { for(const attack_type& a : args.u.attacks()) { if(a.matches_filter(c.get_parsed_config())) { return true; } } return false; }); } else { std::stringstream errmsg; errmsg << "encountered a child [" << child.first << "] of a standard unit filter, it is being ignored"; DBG_CF << errmsg.str() << std::endl; } } }
void part::resolve_wml(const vconfig &cfg) { if(cfg.null()) { return; } // Converts shortcut syntax to members of [background_layer] background_layer bl; if(cfg.has_attribute("background")) { bl.set_file(cfg["background"].str()); } if(cfg.has_attribute("scale_background")) { bl.set_scale_horizontally(cfg["scale_background"].to_bool(true)); bl.set_scale_vertically(cfg["scale_background"].to_bool(true)); } else { if(cfg.has_attribute("scale_background_vertically")) { bl.set_scale_vertically(cfg["scale_background_vertically"].to_bool(true)); } if(cfg.has_attribute("scale_background_horizontally")) { bl.set_scale_horizontally(cfg["scale_background_horizontally"].to_bool(true)); } } if(cfg.has_attribute("tile_background")) { bl.set_tile_horizontally(cfg["tile_background"].to_bool(false)); bl.set_tile_vertically(cfg["tile_background"].to_bool(false)); } else { if(cfg.has_attribute("tile_background_vertically")) { bl.set_tile_vertically(cfg["tile_background_vertically"].to_bool(false)); } if(cfg.has_attribute("tile_background_horizontally")) { bl.set_tile_vertically(cfg["tile_background_horizontally"].to_bool(false)); } } if(cfg.has_attribute("keep_aspect_ratio")) { bl.set_keep_aspect_ratio(cfg["keep_aspect_ratio"].to_bool(true)); } background_layers_.push_back(bl); if(cfg.has_attribute("show_title")) { show_title_ = cfg["show_title"].to_bool(); } if(cfg.has_attribute("story")) { text_ = cfg["story"].str(); } if(cfg.has_attribute("title")) { text_title_ = cfg["title"].str(); if(!cfg.has_attribute("show_title")) { show_title_ = true; } } if(cfg.has_attribute("text_layout")) { text_block_loc_ = string_tblock_loc(cfg["text_layout"]); } if(cfg.has_attribute("title_alignment")) { title_alignment_ = string_title_align(cfg["title_alignment"]); } if(cfg.has_attribute("music")) { music_ = cfg["music"].str(); } if(cfg.has_attribute("sound")) { sound_ = cfg["sound"].str(); } // Execution flow/branching/[image] for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++ i) { // i->first and i->second are goddamn temporaries; do not make references const std::string key = i->first; const vconfig node = i->second; // [background_layer] if (key == "background_layer") { background_layers_.push_back(node.get_parsed_config()); } // [image] else if(key == "image") { floating_images_.push_back(node.get_parsed_config()); } // [if] else if(key == "if") { // check if the [if] tag has a [then] child; // if we try to execute a non-existing [then], we get a segfault if (game_events::conditional_passed(node)) { if (node.has_child("then")) { resolve_wml(node.child("then")); } } // condition not passed, check [elseif] and [else] else { // get all [elseif] children and set a flag vconfig::child_list elseif_children = node.get_children("elseif"); bool elseif_flag = false; // for each [elseif]: test if it has a [then] child // if the condition matches, execute [then] and raise flag for (vconfig::child_list::const_iterator elseif = elseif_children.begin(); elseif != elseif_children.end(); ++elseif) { if (game_events::conditional_passed(*elseif)) { if (elseif->has_child("then")) { resolve_wml(elseif->child("then")); } elseif_flag = true; break; } } // if we have an [else] tag and no [elseif] was successful (flag not raised), execute it if (node.has_child("else") && !elseif_flag) { resolve_wml(node.child("else")); } } } // [switch] else if(key == "switch") { const std::string var_name = node["variable"]; const std::string var_actual_value = resources::gamedata->get_variable_const(var_name); bool case_not_found = true; for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) { if(j->first != "case") continue; // Enter all matching cases. const std::string var_expected_value = (j->second)["value"]; if(var_actual_value == var_expected_value) { case_not_found = false; resolve_wml(j->second); } } if(case_not_found) { for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) { if(j->first != "else") continue; // Enter all elses. resolve_wml(j->second); } } } // [deprecated_message] else if(key == "deprecated_message") { // Won't appear until the scenario start event finishes. lg::wml_error() << node["message"] << '\n'; } // [wml_message] else if(key == "wml_message") { // As with [deprecated_message], // it won't appear until the scenario start event is complete. resources::game_events->pump().put_wml_message(node["logger"],node["message"],node["in_chat"]); } } }
bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_location& loc, const unit* u2) const { if (!vcfg["name"].blank() && vcfg["name"].t_str() != u.name()) { return false; } if (!vcfg["id"].empty()) { std::vector<std::string> id_list = utils::split(vcfg["id"]); if (std::find(id_list.begin(), id_list.end(), u.id()) == id_list.end()) { return false; } } // Allow 'speaker' as an alternative to id, since people use it so often if (!vcfg["speaker"].blank() && vcfg["speaker"].str() != u.id()) { return false; } if (vcfg.has_child("filter_location")) { if (vcfg.count_children("filter_location") > 1) { FAIL("Encountered multiple [filter_location] children of a standard unit filter. " "This is not currently supported and in all versions of wesnoth would have " "resulted in the later children being ignored. You must use [and] or similar " "to achieve the desired result."); } terrain_filter filt(vcfg.child("filter_location"), &fc_, use_flat_tod_); if (!filt.match(loc)) { return false; } } if(vcfg.has_child("filter_side")) { if (vcfg.count_children("filter_side") > 1) { FAIL("Encountered multiple [filter_side] children of a standard unit filter. " "This is not currently supported and in all versions of wesnoth would have " "resulted in the later children being ignored. You must use [and] or similar " "to achieve the desired result."); } side_filter filt(vcfg.child("filter_side"), &fc_); if(!filt.match(u.side())) return false; } // Also allow filtering on location ranges outside of the location filter if (!vcfg["x"].blank() || !vcfg["y"].blank()){ if(vcfg["x"] == "recall" && vcfg["y"] == "recall") { //locations on the map are considered to not be on a recall list if (fc_.get_disp_context().map().on_board(loc)) { return false; } } else if(vcfg["x"].empty() && vcfg["y"].empty()) { return false; } else if(!loc.matches_range(vcfg["x"], vcfg["y"])) { return false; } } // The type could be a comma separated list of types if (!vcfg["type"].empty()) { std::vector<std::string> types = utils::split(vcfg["type"]); if (std::find(types.begin(), types.end(), u.type_id()) == types.end()) { return false; } } // Shorthand for all advancements of a given type if (!vcfg["type_tree"].empty()) { std::set<std::string> types; for(const std::string type : utils::split(vcfg["type_tree"])) { if(types.count(type)) { continue; } if(const unit_type* ut = unit_types.find(type)) { const auto& tree = ut->advancement_tree(); types.insert(tree.begin(), tree.end()); types.insert(type); } } if(types.find(u.type_id()) == types.end()) { return false; } } // The variation_type could be a comma separated list of types if (!vcfg["variation"].empty()) { std::vector<std::string> types = utils::split(vcfg["variation"]); if (std::find(types.begin(), types.end(), u.variation()) == types.end()) { return false; } } // The has_variation_type could be a comma separated list of types if (!vcfg["has_variation"].empty()) { bool match = false; // If this unit is a variation itself then search in the base unit's variations. const unit_type* const type = u.variation().empty() ? &u.type() : unit_types.find(u.type().base_id()); assert(type); for (const std::string& variation_id : utils::split(vcfg["has_variation"])) { if (type->has_variation(variation_id)) { match = true; break; } } if (!match) return false; } if (!vcfg["ability"].empty()) { bool match = false; for (const std::string& ability_id : utils::split(vcfg["ability"])) { if (u.has_ability_by_id(ability_id)) { match = true; break; } } if (!match) return false; } if (!vcfg["race"].empty()) { std::vector<std::string> races = utils::split(vcfg["race"]); if (std::find(races.begin(), races.end(), u.race()->id()) == races.end()) { return false; } } if (!vcfg["gender"].blank() && string_gender(vcfg["gender"]) != u.gender()) { return false; } if (!vcfg["side"].empty() && vcfg["side"].to_int(-999) != u.side()) { std::vector<std::string> sides = utils::split(vcfg["side"]); const std::string u_side = std::to_string(u.side()); if (std::find(sides.begin(), sides.end(), u_side) == sides.end()) { return false; } } // handle statuses list if (!vcfg["status"].empty()) { bool status_found = false; for (const std::string status : utils::split(vcfg["status"])) { if(u.get_state(status)) { status_found = true; break; } } if(!status_found) { return false; } } if (vcfg.has_child("has_attack")) { const vconfig& weap_filter = vcfg.child("has_attack"); bool has_weapon = false; for(const attack_type& a : u.attacks()) { if(a.matches_filter(weap_filter.get_parsed_config())) { has_weapon = true; break; } } if(!has_weapon) { return false; } } else if (!vcfg["has_weapon"].blank()) { std::string weapon = vcfg["has_weapon"]; bool has_weapon = false; for(const attack_type& a : u.attacks()) { if(a.id() == weapon) { has_weapon = true; break; } } if(!has_weapon) { return false; } } if (!vcfg["role"].blank() && vcfg["role"].str() != u.get_role()) { return false; } if (!vcfg["ai_special"].blank() && ((vcfg["ai_special"].str() == "guardian") != u.get_state(unit::STATE_GUARDIAN))) { return false; } if (!vcfg["canrecruit"].blank() && vcfg["canrecruit"].to_bool() != u.can_recruit()) { return false; } if (!vcfg["recall_cost"].blank() && vcfg["recall_cost"].to_int(-1) != u.recall_cost()) { return false; } if (!vcfg["level"].blank() && vcfg["level"].to_int(-1) != u.level()) { return false; } if (!vcfg["defense"].blank() && vcfg["defense"].to_int(-1) != u.defense_modifier(fc_.get_disp_context().map().get_terrain(loc))) { return false; } if (!vcfg["movement_cost"].blank() && vcfg["movement_cost"].to_int(-1) != u.movement_cost(fc_.get_disp_context().map().get_terrain(loc))) { return false; } // Now start with the new WML based comparison. // If a key is in the unit and in the filter, they should match // filter only => not for us // unit only => not filtered config unit_cfg; // No point in serializing the unit once for each [filter_wml]! for (const vconfig& wmlcfg : vcfg.get_children("filter_wml")) { config fwml = wmlcfg.get_parsed_config(); /* Check if the filter only cares about variables. If so, no need to serialize the whole unit. */ config::all_children_itors ci = fwml.all_children_range(); if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") { if (!u.variables().matches(ci.front().cfg)) return false; } else { if (unit_cfg.empty()) u.write(unit_cfg); if (!unit_cfg.matches(fwml)) return false; } } for (const vconfig& vision : vcfg.get_children("filter_vision")) { std::set<int> viewers; // Use standard side filter side_filter ssf(vision, &fc_); std::vector<int> sides = ssf.get_teams(); viewers.insert(sides.begin(), sides.end()); bool found = false; for (const int viewer : viewers) { bool fogged = fc_.get_disp_context().teams()[viewer - 1].fogged(loc); bool hiding = u.invisible(loc, fc_.get_disp_context()); bool unit_hidden = fogged || hiding; if (vision["visible"].to_bool(true) != unit_hidden) { found = true; break; } } if (!found) {return false;} } if (vcfg.has_child("filter_adjacent")) { const unit_map& units = fc_.get_disp_context().units(); map_location adjacent[6]; get_adjacent_tiles(loc, adjacent); for (const vconfig& adj_cfg : vcfg.get_children("filter_adjacent")) { int match_count=0; unit_filter filt(adj_cfg, &fc_, use_flat_tod_); config::attribute_value i_adjacent = adj_cfg["adjacent"]; std::vector<map_location::DIRECTION> dirs; if (i_adjacent.blank()) { dirs = map_location::default_dirs(); } else { dirs = map_location::parse_directions(i_adjacent); } std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end(); for (j = dirs.begin(); j != j_end; ++j) { unit_map::const_iterator unit_itor = units.find(adjacent[*j]); if (unit_itor == units.end() || !filt(*unit_itor, u)) { continue; } boost::optional<bool> is_enemy; if (!adj_cfg["is_enemy"].blank()) { is_enemy = adj_cfg["is_enemy"].to_bool(); } if (!is_enemy || *is_enemy == fc_.get_disp_context().teams()[u.side() - 1].is_enemy(unit_itor->side())) { ++match_count; } } static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6"); config::attribute_value i_count = adj_cfg["count"]; if(!in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts)) { return false; } } } if (!vcfg["find_in"].blank()) { // Allow filtering by searching a stored variable of units if (const game_data * gd = fc_.get_game_data()) { try { variable_access_const vi = gd->get_variable_access_read(vcfg["find_in"]); bool found_id = false; for (const config& c : vi.as_array()) { if(c["id"] == u.id()) found_id = true; } if(!found_id) { return false; } } catch(const invalid_variablename_exception&) { return false; } } } if (!vcfg["formula"].blank()) { try { const unit_callable main(loc,u); game_logic::map_formula_callable callable(&main); if (u2) { std::shared_ptr<unit_callable> secondary(new unit_callable(*u2)); callable.add("other", variant(secondary.get())); // It's not destroyed upon scope exit because the variant holds a reference } const game_logic::formula form(vcfg["formula"]); if(!form.evaluate(callable).as_bool()) { return false; } return true; } catch(game_logic::formula_error& e) { lg::wml_error() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n"; // Formulae with syntax errors match nothing return false; } } if (!vcfg["lua_function"].blank()) { if (game_lua_kernel * lk = fc_.get_lua_kernel()) { bool b = lk->run_filter(vcfg["lua_function"].str().c_str(), u); if (!b) return false; } } return true; }
void part::resolve_wml(const vconfig &cfg) { if(cfg.null()) { return; } if(cfg.has_attribute("background")) { background_file_ = cfg["background"].str(); } if(cfg.has_attribute("scale_background")) { scale_background_ = cfg["scale_background"].to_bool(true); } if(cfg.has_attribute("show_title")) { show_title_ = cfg["show_title"].to_bool(); } if(cfg.has_attribute("story")) { text_ = cfg["story"].str(); } if(cfg.has_attribute("title")) { text_title_ = cfg["title"].str(); if(!cfg.has_attribute("show_title")) { show_title_ = true; } } if(cfg.has_attribute("text_layout")) { text_block_loc_ = string_tblock_loc(cfg["text_layout"]); } if(cfg.has_attribute("title_alignment")) { title_alignment_ = string_title_align(cfg["title_alignment"]); } if(cfg.has_attribute("music")) { music_ = cfg["music"].str(); } if(cfg.has_attribute("sound")) { sound_ = cfg["sound"].str(); } // Execution flow/branching/[image] for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++ i) { // i->first and i->second are goddamn temporaries; do not make references const std::string key = i->first; const vconfig node = i->second; // [image] if(key == "image") { floating_images_.push_back(node.get_parsed_config()); } // [if] else if(key == "if") { const std::string branch_label = game_events::conditional_passed(node) ? "then" : "else"; if(node.has_child(branch_label)) { const vconfig branch = node.child(branch_label); resolve_wml(branch); } } // [switch] else if(key == "switch") { const std::string var_name = node["variable"]; const std::string var_actual_value = resources::state_of_game->get_variable_const(var_name); bool case_not_found = true; for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) { if(j->first != "case") continue; // Enter all matching cases. const std::string var_expected_value = (j->second)["value"]; if(var_actual_value == var_expected_value) { case_not_found = false; resolve_wml(j->second); } } if(case_not_found) { for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) { if(j->first != "else") continue; // Enter all elses. resolve_wml(j->second); } } } // [deprecated_message] else if(key == "deprecated_message") { // Won't appear until the scenario start event finishes. game_events::handle_deprecated_message(node.get_parsed_config()); } // [wml_message] else if(key == "wml_message") { // Pass to game events handler. As with [deprecated_message], // it won't appear until the scenario start event is complete. game_events::handle_wml_log_message(node.get_parsed_config()); } } }
void controller::resolve_wml(const vconfig& cfg) { for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i) { // i->first and i->second are goddamn temporaries; do not make references const std::string key = i->first; const vconfig node = i->second; if(key == "part" && !node.empty()) { part_pointer_type const story_part(new part(node)); // Use scenario name as part title if the WML doesn't supply a custom one. if((*story_part).show_title() && (*story_part).title().empty()) { (*story_part).set_title( scenario_name_ ); } parts_.push_back(story_part); } // [if] else if(key == "if") { // check if the [if] tag has a [then] child; // if we try to execute a non-existing [then], we get a segfault if (game_events::conditional_passed(node)) { if (node.has_child("then")) { resolve_wml(node.child("then")); } } // condition not passed, check [elseif] and [else] else { // get all [elseif] children and set a flag vconfig::child_list elseif_children = node.get_children("elseif"); bool elseif_flag = false; // for each [elseif]: test if it has a [then] child // if the condition matches, execute [then] and raise flag for (vconfig::child_list::const_iterator elseif = elseif_children.begin(); elseif != elseif_children.end(); ++elseif) { if (game_events::conditional_passed(*elseif)) { if (elseif->has_child("then")) { resolve_wml(elseif->child("then")); } elseif_flag = true; break; } } // if we have an [else] tag and no [elseif] was successful (flag not raised), execute it if (node.has_child("else") && !elseif_flag) { resolve_wml(node.child("else")); } } } // [switch] else if(key == "switch") { const std::string var_name = node["variable"]; const std::string var_actual_value = resources::gamedata->get_variable_const(var_name); bool case_not_found = true; for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) { if(j->first != "case") continue; // Enter all matching cases. const std::string var_expected_value = (j->second)["value"]; if(var_actual_value == var_expected_value) { case_not_found = false; resolve_wml(j->second); } } if(case_not_found) { for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) { if(j->first != "else") continue; // Enter all elses. resolve_wml(j->second); } } } // [deprecated_message] else if(key == "deprecated_message") { // Won't appear until the scenario start event finishes. game_events::handle_deprecated_message(node.get_parsed_config()); } // [wml_message] else if(key == "wml_message") { // Pass to game events handler. As with [deprecated_message], // it won't appear until the scenario start event is complete. game_events::handle_wml_log_message(node.get_parsed_config()); } } }