基于条件的动作(Conditional Actions)

一、概述

如果满足特定条件,这些动作就会被执行。这里不叙述动作,主要说条件。条件是由一些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书写格式

  1. 要等于某个数字。count=<数字>。count=2表示要存在2个单位。
  2. 某段范围内的任一个数字。count=<上限>-<下限>。count=2-59表示只要存在“>=2并且<=59”个单位。
  3. 数段范围内的任一个数字。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中的条件块几点说明:

  1. 条件块标签能是能是[if]。
  2. 一个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;
}

全部评论: 0

    写评论: