一、概述
如果满足特定条件,这些动作就会被执行。这里不叙述动作,主要说条件。条件是由一些WML块来描述的,它可用于[side]和[event]块。
[if]
如果满足这个块包含的条件,执行相应动作。它底下会有:
- 条件块:描述条件。如果条件满足则执行[then]块内动作,否则执行[else]内作动作。
- [then]块:描述动作。如果条件满足就执行它描述的动作。
- [else]块:描述动作。如果条件不满足就执行它描述的动作。
以下是一个使用[if]例子:
Example-1 [if] [variable] name=player equals=zhouyu [/variable] [then] controller=ai [/then] [else] controller=human [/else] [/if]
[variable]是条件块,它的条件判断是player是否等于zhouyu。如果player等于zhouyu,执行[then]块中动作:controller=ai,否则执行controller=human。
[switch]
待看.....http://wiki.wesnoth.org/ConditionalActionsWML#Condition_Tags
[while]
待看.....http://wiki.wesnoth.org/ConditionalActionsWML#Condition_Tags
1.1 条件块
条件块用于描述要执行特定动作必须满足的条件,像Example-1的[variable]。程序没法无限制表示条件,支持以下这三种格式的条件块。
[have_unit]
如果存在一个或count个HP不为零且满足StandardUnitFilter指定条件的单位,这块判断结果为TRUE
- StandardUnitFilter:标准单位筛选设定。
- count(可选):如果指定,则要count个满足StandardUnitFilter的单位这条件才返回TRUE。在值上,它可能是一个数字、范围、或逗号隔开的范围。如果不指定,则使用1-99999,也就是说只要有一个满足StandardUnitFilter的单位这条件就返回TRUE。
注:count书写格式
- 要等于某个数字。count=<数字>。count=2表示要存在2个单位。
- 某段范围内的任一个数字。count=<上限>-<下限>。count=2-59表示只要存在“>=2并且<=59”个单位。
- 数段范围内的任一个数字。count=<上限1>-<下限1>,<上限2>-<下限2>,...。count=1,10-15,245-589,998表示只要存在1个或“>=10并且<=15”个或“>=245并且<=589”个或998个单位。
[have_location]
如果存在满足StandardUnitFilter指定条件的格子,这块判断结果为TRUE。
- StandardLocationFilter:格子选择设定。
- count(可选):意义类似[have_unit]中的count。
[variable]
测试一个WML变量和一个给定值,把这个结果作为这个块的判断结果。
name:要被测试的WML变量,例如以Example-1中的player。
<比较>:比较可以使用下面列出的关键字,说明时$name表示WML变量,Example-1中就是player;value指此刻传下来的值,Example-1中是zhouyu。
- contains:$name内容包含value。
- equals:$name字符串等于value。
- not_equals:$name字符串不等于value。
- numerical_equals:把$name和value都转换为double值,这两个值要相等。
- numerical_not_equals:把$name和value都转换为double值,这两个值要不相等。
- 注:相比equals,numerial_equals和boolean_equals效率要低。举个例子,“1”和“1.0”,用equals时它们是不相等,但以numerial_equals它们是相等的;“yes”和“on”,用equals时它们是不相等,但以boolean_equals它们是相等的。(这同时解释了为什么equals执行更快。用equals在直观上就可以看出是否相等,而不用理解这个值是怎么个写法,像这个WML变量是boolean型)
- greater_than:把$name和value都转换为double值,$name>value。
- greater_than_equal_to:把$name和value都转换为double值,$name>=value。
- less_than:把$name和value都转换为double值,$name<value。
- less_than_equal_to:把$name和value都转换为double值,$name<=value。
- boolean_equals:把$name和value都转换为bool值,$name等于value。
- boolean_not_equal:把$name和value都转换为bool值,$name不等于value。
布尔变量值
当一个WML变量要被解释为boolean类型时,这看作是false还是ture时的值。
看作fase的字符串值:no,fase,off,0,0.0,(uninitialized);
看作true的字符串值:yes,true,on,1,0.1,(任何一人非零数字);
1.2 准条件标签
这些标签不是“真正”的条件。它们包在条件块外头,把这些条件块组合成一个新的条件。这个新的条件要返回ture,可能是它子条件都是true(and时),可能是子条件只要有一个true(or时),可能是子条件是false(not时)。在书写这个准条件标签到时要注意顺序,它们是以着这个条件顺序被处理的。另外要注意,第一个条件块外头不应该有or标签,
[and]
与。条件要判断结果是true,除了其它子块是true,这块也须是true。
[or]
或。条件要判断结果是true,只要这子块是true。
[not]
非。条件要判断结果是true,这子块是false。
二、[side]中的条件块
[side]用于描述阵营,针对玩家选择角色不同阵营描述往往不用,像玩家选周瑜时,周瑜阵营的控制属性human,玩家选排周瑜时,这阵营控制属性就是ai。为一个[side]就要实现这种区别描述,此时就须要用条件块。
[side] side=3 [if] [variable] name=player equals=zhouyu [/variable] [then] controller=human gold=150 income=0 [/then] [else] [if] [variable] name=player equals=zhouyu [/variable] [then] controller=ai gold=200 income=80 [/then] [else] controller=ai gold=300 income=80 [/else] [/if] [/else] [/if] ...... [/side]
玩家选周瑜时,控制属性是human,初始金150,每回合入库金0。玩家选刘备时,控制属性是ai,初始金200,每回合入库金80。玩家选的既不是周瑜又不是刘备时,控制属性是ai,初始金300,每回合入库金80。
对于side中的条件块几点说明:
- 条件块标签能是能是[if]。
- 一个side可以有多个[if]块。
三、[event]中的条件块
[event]用于事件,有时须在事件中跟据当前状态来对事件条件执行不同处理,例如常见的选择角色。
以下是战役胜利事件
[event] name=last breath first_time_only=no [filter] last_city=yes [/filter] [if] [variable] name=player equals=zhouyu [/variable] [and] [variable] name=unit.side equals=2 [/variable] [/and] [then] [message] speaker=6 message= _ "stop word: 1" [/message] [message] speaker=39 message= _ "stop word: 2" [/message] [kill] master_hero=39 animate=yes [/kill] [endlevel] result=victory [/endlevel] [/then] [else] [if] [variable] name=player equals=liubei [/variable] [and] [variable] name=unit.side equals=2 [/variable] [/and] [then] [message] speaker=4 message= _ "stop word: 1" [/message] [message] speaker=39 message= _ "stop word: 2" [/message] [kill] master_hero=39 animate=yes [/kill] [endlevel] result=victory [/endlevel] [/then] [else] [if] [variable] name=player equals=caoren [/variable] [and] [variable] name=unit.side equals=3 [/variable] [/and] [then] [message] speaker=39 message= _ "caoren stop word: 1" [/message] [message] speaker=6 message= _ "caoren stop word: 2" [/message] [kill] master_hero=6 animate=yes [/kill] [endlevel] result=victory [/endlevel] [/then] [/if] [/else] [/if] [/else] [/if] [/event]
当曹仁阵营(阵营2)最后一个城市被攻陷时。满足last_city=yes筛选条件,这事件要被触发。
玩家是周瑜,执行:
[message] speaker=6 message= _ "stop word: 1" [/message] [message] speaker=39 message= _ "stop word: 2" [/message] [kill] master_hero=39 animate=yes [/kill] [endlevel] result=victory [/endlevel]
玩家是刘备,执行:
[message] speaker=4 message= _ "stop word: 1" [/message] [message] speaker=39 message= _ "stop word: 2" [/message] [kill] master_hero=39 animate=yes [/kill] [endlevel] result=victory [/endlevel]
对于event中的条件块几点说明:
一、条件块中不要包含[filter],条件块中[filter]不会产生筛选作用。
四、函数:conditional_passed
@cond:[if]块。Example-1中,它child就是[variable]、[then]和[else]块,而值映射是空。
@backwards_compat:false。(可能是个为向后兼容而加的变量)
返回值:
true:满足条件
false:不满足条件
bool conditional_passed(const unit_map* units, const vconfig cond, bool backwards_compat) { bool allow_backwards_compat = backwards_compat = backwards_compat && utils::string_bool(cond["backwards_compat"], true); bool matches = internal_conditional_passed(units, cond, allow_backwards_compat); // Handle [and], [or], and [not] with in-order precedence int or_count = 0; vconfig::all_children_iterator cond_i = cond.ordered_begin(); vconfig::all_children_iterator cond_end = cond.ordered_end(); while (cond_i != cond_end) { const std::string& cond_name = cond_i.get_key(); const vconfig& cond_filter = cond_i.get_child(); // Example-1时这里会被进入三次,#0:cond_name=variable,#1:cond_name=then,#2:cond_name=else if (cond_name == "and") { // Handle [and] // 这里调用的是conditional_passed而不是internal_conditional_passed,也就是说准备条标签下可以再有准条件标签 matches = matches && conditional_passed(units, cond_filter, backwards_compat); backwards_compat = false; } else if(cond_name == "or") { // Handle [or] matches = matches || conditional_passed(units, cond_filter, backwards_compat); ++or_count; } else if(cond_name == "not") { // Handle [not] matches = matches && !conditional_passed(units, cond_filter, backwards_compat); backwards_compat = false; } ++cond_i; } // Check for deprecated [or] syntax // 从代码上看,以下是重新解释了[or]块,使用规则:所有[or]须要同时是true,结果才是true。这个规则和通常认为是不一致的,不知wesnoth过去代码是以这规则来的。 if (matches && or_count > 1 && allow_backwards_compat) { lg::wml_error << "possible deprecated [or] syntax: now forcing re-interpretation\n"; /** * @todo For now we will re-interpret it according to the old * rules, but this should be later to prevent re-interpretation * errors. */ const vconfig::child_list& orcfgs = cond.get_children("or"); for (unsigned int i=0; i < orcfgs.size(); ++i) { if (conditional_passed(units, orcfgs[i])) { return true; } } return false; } return matches; }
五、函数:internal_conditional_passed
static bool internal_conditional_passed(const unit_map* units, const vconfig cond, bool& backwards_compat) { static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-99999"); // 检查第一种允许的条件块:[have_unit] // If the if statement requires we have a certain unit, // then check for that. const vconfig::child_list& have_unit = cond.get_children("have_unit"); backwards_compat = backwards_compat && have_unit.empty(); for (vconfig::child_list::const_iterator u = have_unit.begin(); u != have_unit.end(); ++u) { if (units == NULL) return false; std::vector<std::pair<int,int> > counts = (*u).has_attribute("count")? utils::parse_ranges((*u)["count"]) : default_counts; int match_count = 0; unit_map::const_iterator itor; for (itor = units->begin(); itor != units->end(); ++itor) { if (itor->second.hitpoints() > 0 && game_events::unit_matches_filter(itor, *u)) { ++match_count; if (counts == default_counts) { // by default a single match is enough, so avoid extra work break; } } } if (!in_ranges(match_count, counts)) { return false; } } // 检查第三种允许的条件块:[have_location] // If the if statement requires we have a certain location, // then check for that. const vconfig::child_list& have_location = cond.get_children("have_location"); backwards_compat = backwards_compat && have_location.empty(); for (vconfig::child_list::const_iterator v = have_location.begin(); v != have_location.end(); ++v) { std::set<map_location> res; terrain_filter(*v, *units).get_locations(res); std::vector<std::pair<int,int> > counts = (*v).has_attribute("count")? utils::parse_ranges((*v)["count"]) : default_counts; if (!in_ranges<int>(res.size(), counts)) { return false; } } // 检查第三种允许的条件块:[variable] // Check against each variable statement, // to see if the variable matches the conditions or not. const vconfig::child_list& variables = cond.get_children("variable"); backwards_compat = backwards_compat && variables.empty(); foreach (const vconfig &values, variables) { const std::string name = values["name"]; const std::string& value = resources::state_of_game->get_variable_const(name); const double num_value = atof(value.c_str()); #define TEST_STR_ATTR(name, test) do { \ if (values.has_attribute(name)) { \ std::string attr_str = values[name].str(); \ if (!(test)) return false; \ } \ } while (0) #define TEST_NUM_ATTR(name, test) do { \ if (values.has_attribute(name)) { \ double attr_num = atof(values[name].c_str()); \ if (!(test)) return false; \ } \ } while (0) TEST_STR_ATTR("equals", value == attr_str); TEST_NUM_ATTR("numerical_equals", num_value == attr_num); TEST_STR_ATTR("not_equals", value != attr_str); TEST_NUM_ATTR("numerical_not_equals", num_value != attr_num); TEST_NUM_ATTR("greater_than", num_value > attr_num); TEST_NUM_ATTR("less_than", num_value < attr_num); TEST_NUM_ATTR("greater_than_equal_to", num_value >= attr_num); TEST_NUM_ATTR("less_than_equal_to", num_value <= attr_num); TEST_STR_ATTR("boolean_equals", utils::string_bool(value) == utils::string_bool(attr_str)); TEST_STR_ATTR("boolean_not_equals", utils::string_bool(value) != utils::string_bool(attr_str)); TEST_STR_ATTR("contains", value.find(attr_str) != std::string::npos); #undef TEST_STR_ATTR #undef TEST_NUM_ATTR } // 3/2/1种条件块都满足,返回true return true; }