SDL中文论坛

标题: WML标签:event [打印本页]

作者: ancientcc    时间: 2020-11-29 10:51
标题: WML标签:event

游戏中事件可分为两种,SDL事件和游戏事件。SDL事件指由SDL系统产生的事件,像鼠标左键被按下,键盘R键被按下。游戏事件指玩游戏时产生的事件,像攻战了某一城市,某一部队被消灭。[event]块对应的是游戏事件。

二楼:处理事件顶层逻辑
三楼:触发条件
作者: ancientcc    时间: 2020-11-29 10:52
标题: 处理事件顶层逻辑
程序(C++)内部有个可处理事件集,它定义了游戏支持的所有事件类型,具体是事件的触发时机、类型名称。MOD关卡(WML)内部有个可处理事件集,它定义了此个关卡关心的事件名称、触发条件、以及该事件一旦被触发执行的操作。玩家在玩游戏过程中满足触发时机时,C++向事件处理模块(程序内一个模块)抛出该事件,后者发现正在玩的关卡是关注该事件并满足触发条件,于是就调用关卡中定义的操作。

以刘备传之长坂坡之战中两个事件来进一步理解处理事件逻辑。(WML代码见<kingdom-src>/data/campaigns/Legend_of_Bei_Liu/scenarios/01_changbanpo.cfg)

事件attack_end的WML代码
  1. [event]
  2.         name=attack_end
  3.        
  4.         first_time_only=no
  5.         [filter]
  6.                 side=3
  7.         [/filter]
  8.         [filter_second]
  9.                 side=1
  10.                 hp=yes
  11.                 master_hero=50
  12.         [/filter_second]
  13.                
  14.         [message]
  15.                 speaker=43
  16.                 message= _ "My ability can not compare with Cao Cao, I surrender."
  17.         [/message]
  18.         [kill]
  19.                 master_hero=50
  20.                 a_side=3
  21.                 animate=yes
  22.         [/kill]
  23. [/event]
复制代码
C++程序约定一旦攻击结束后会产生attack_end事件,即该事件触发时机是攻击结束后、名称是attack_end。MOD关卡定义了一个name=attack_end的[event]块(见以上WML代码),它表示此个关卡关心attack_end事件,但要执行操作还要满足(即触发条件)攻击方是属于曹操势力(side=3),防御方是属于刘琮势力(side=1)、并且防御单位主将是襄阳(master_hero=50)、攻击后襄阳没被催垮(hp=yes),而一旦满足条件后将执行两个动作,第一个动作是弹出一个消息框([message]),框中内容是刘琮(speaker=43)说“我的才能不及曹操,愿献城投降”,第二个动作是襄阳改为归属攻击方([kill]),即曹操势力。

事件comeinto的WML代码
  1. [event]
  2.         name=comeinto

  3.         [filter]
  4.                 must_heros = 216
  5.         [/filter]
  6.         [filter_second]
  7.                 must_heros = 4
  8.         [/filter_second]
  9.        
  10.         [sideheros]
  11.                 side=2
  12.                 heros=0,4,5,10,31,32,40,42,58,59,60,61,80,81,82,162,209,213
  13.         [/sideheros]
  14.                
  15.         [endlevel]
  16.                 result=victory
  17.         [/endlevel]
  18. [/event]
复制代码
C++程序约定一旦单位(一般是部队)进城后会产生comeinto事件,即该事件触发时机是单位进城后、名称是comeinto。MOD关卡定义了一个name=comeinto的[event]块(见以上WML代码),它表示此个关卡关心comeinto事件,但要执行操作还要满足(即触发条件)进入砦这个城市(must_heros=216)、进入的部队中有刘备这个武将(must_heros = 4),而一旦满足条件后将执行两个动作。第一个动作是调整刘备势力武将([sideheros]),把heros字段指定的武将归属刘备势力,而不是的武将则让退出,第二个动作是触发关卡结束([endlevel]),result=victory表示玩家胜利通过本关卡。
作者: ancientcc    时间: 2020-11-29 10:53
标题: 触发条件
本帖最后由 ancientcc 于 2021-4-25 20:28 编辑

在关卡中定义一个事件,即写了一个event块,什么条件下该事件会被触发?——这个触发条件归纳起来有两条(需要同时满足):C++代码触发了事件、满足[event]中写的条件。

[event]可自写条件,这让同一个关卡能产生同名却不同操作事件。例如对于以上的comeinto事件,已写的comeinto是刘备队进入砦后就让胜利通过本关卡,假如还要支持一个事件:一旦曹操队进入襄阳,就让曹操势力所有部队补满HP,要支持它也是通过写一个name=comeinto的[event]块,只是当中条件、操作不同罢了。

让深入说下C++代码。C++代码对关卡关注的事件有个数组((game_events.cpp)event_handlers),数组内每个元素对应一个[event]块(对于相同name字段那也是不同[evnet]块),一旦触发一个事件,像攻击结束满足触发attack_end时机,它就根据这个事件名以及当前上下文从头到尾去逐个“处理”数组中的[event]。“处理”包括匹配判断和执行操作,匹配判断包括两个步骤,一个是name要一致,二是[event]中条件要满足,如果匹配就执行“处理”的第二步,即执行该[event]中定义的操作;否则不执行定义的操作,继续数组中的下一个[event]块(当然,即使此次匹配满足并且执行了操作,它还是会继续下一个的)。

说说first_time_only字段。在attack_end、comeinto两个[event]中都看到了first_time_only=no,引入first_time_only是为了提高C++处理事件效率。从以上C++代码如何处理事件可以看出,C++代码一旦时机一到抛出一事件了,它就在关卡注册的[event]数组中找,这个找是要整数组逐个元素地处理!针对这种逐个处理方式让考虑种情况,假如有那么个事件它是只要处理过一次,此个关卡就不需要再处理了,那为了提高效率可以处理该事件后就把该[event]块从数组中删除,接下逐个处理时就可以少去这个已是肯定冗余的操作。first_time_only就用于这个目的,first_time_only=yes时表示这是一个一次性事件,处理过一次就可以从数组中删除了,no则指示这是个重复性事件,first_time_only默认值是yes,即[event]块没有first_time_only时表示它是一个一次性事件。

明确下first_time_only中的执行概念,它的一次执行是指满足两条件后执行那些个操作,像comeinto中的[sideheros]、[endlevel],不是C++只要抛出name对应的事件就算执行。

事件上下文
知道了条件包括时机和[event]块中写的条件,对于两条件中时机较好理解,C++程序会给出这个时机,那[event]块中条件该怎么写?很显然,这个条件往往是和当前状态相关,像attack_end,它往往和攻击方、防御方相关,要能让关卡编写者写出“好”条件就应该有办法让他们知道事件产生时攻击方和防御方细节(更进一步的话还有攻击时采用的战法等),即事件触发时环境,称之为事件上下文。对attack_end上下文是攻击方、防御方,换到comeinto就是进入到城市、进入的部队,而这个上下文由谁提供,无疑是C++代码,C++代码得有一种方法让关卡编写者访问到事件上下文。

由以上C++对事件上下文处理可看出,编写关卡时要利用上下文写“好”条件,就需要知道各个事件下C++如何定义[filter]、[second_filter]内容,以及[filter]/[sencond_filter]内容许出现的字段。对前一部分,不同事件时都可说不同的,对后一部分,即[filter]/[sencond_filter]内容许出现的字段,由于两个块都表示单位,对不同事件还是较有统一性的。以下是各字段小结。

字段名值(默认值)[空值]注1
hpyes/no(无要求)[no]yes时要求单位hp>0,no时则无所谓
must_heros以“,”组合的武将编号(无要求)[无要求]单位中须存在编号中的武将
speaker等同master_hero,将废弃
last_cityyes/no(无要求)一旦存在,要求单位须是城市。yes时要求它是该势力最后一个城市
exist_reside_troop武将编号(无要求)一旦存在,要求单位须是城市。城市存在武将(不必一定是主将)是武将编号的部队
id字符串(无要求)将废弃
filter_locationWML块单位所在地形
xx坐标范围(无要求)单位所在x坐标满足坐标范围
yy坐标范围(无要求)单位所在y坐标满足坐标范围
type字符串(无要求)一旦存在,须是有效的兵种。单位须是字符串指定的兵种
ability以“,”组合的字符串(无要求)一旦存在,须是单位技能标识。单位须有字符串指定指能中的一种
race字符串(无要求)单位须是字符串标识的种族
gender字符串(无要求)单位须是字符串标识的性别
side以“,”组合的势力编号(无要求)单位所属势力需在编号中
has_weapon以“,”组合的战法名称(无要求)单位拥有组合中的一种战法
role 将废弃
ai_special 将废弃
canrecruit 将废弃
level数值(无要求)[-1]单位等级须是数值
defense数值(无要求)[-1]单位在该格子上防御力须等于数值
movement_cost数值(无要求)[-1]单位在该格子上消耗移动力须等于数值
filter_wmlWML块
filter_visionWML块
filter_adjacentWML块
find_in 将废弃
formula 将废弃
lua_function 将废弃

注:
1:默认值指的是[filter]、[second_filter]块不存在该字段时C++会赋给的值,空值指的是存在该字段但把该字段等于空值时C++会赋给的值
作者: ancientcc    时间: 2020-11-29 10:53
[store_unit]
功能:把单位存进一个变量。

当前状态下如果满足filter的单位能有多个,那么这些个单位就会被存进变量,它们以数组形式被引用。对于要引用,其实都应该是数组形式,只是当只存有一个单位时,固定用索引0而已。

注:store_unit一旦存下单位,它会在lua子系统生成一个unit对象。这个unit对象要等到lua退出时才释放(play_controller析构函数中),也就是说删除变量不会释入这个unit对象。
  1. [store_unit]
  2.         [filter]
  3.                 cityno = 1
  4.                 index = 0
  5.         [/filter]
  6.         variable = reside
  7. [/store_unit]
  8. {TARGET_TYPE $reside[0].type}
复制代码
此个[store_unit]作用是把cityno=1这个城市的第一只城内部队存入reside这个变量。如果之前已有reside变量,首先清除(mode=always_clear)。cityno=1城市中有城内部队的话,reside变量会存也只会存一只部队,但在引用时需使用$reside[0]这种数组格式。

clear_value可以删除多个单位的数组变量。
作者: ancientcc    时间: 2020-11-29 10:53
标题: 触发后执行的操作
回看下attack_end、comeinto事件,一旦满足条件后,attack_end会执行两个操作,显示消息框([message])、城市归属到曹操势力([kill]),comeinto则是调整刘备势力内武将([sideheros])、触发通过本关卡([endlevel])。当然,只要符合自个关卡设计要求,你也可以把[sideheros]、[endlevel]中的一个或两个写入attack_end。对触发后执行的操作,C++(严格说还有lua,参考底下“lua”)定义了可使用的操作原子,像[message]、[kill]、[sideheros]、[endlevel],关卡编写者按自个要求使用原子,游戏不限制一个事件执行的操作可执行哪些原子、多少个原子。

一个操作原子对应一个WML块,C++程序内定了块叫什么(即操作原子叫什么)、执行什么功能、块中可存在哪些字段、以及字段可取什么值。块中存在的字段叫操作原子参数,对每种操作它会自定义参数。C++内定操作有不少,将来会不断补充。

lua
注:以下要叙内容涉及到lua、还需要理解游戏中处理事件的C++代码,相对来说较为难懂,对普通关卡编写者来说可不去理解此个部分(不理解也已能写[event]块),对要理解此中内容的请结合“第三章 WML和Lua”中的“1.4 C和Lua相互调用”。

游戏为让编写MOD更灵活,可使用lua编写事件,在处理事件方面,lua代码主要实现两个功能:执行事件通用逻辑、实现部分操作原子的私有逻辑。

实现操作原子私有逻辑,有些是全由C++完成,有些是lua、C++共同完成(这里C++实现的是库代码功能,它被lua调用,因而往往说是lua完成了操作原子的私有逻辑)。哪些原子是C++完成,哪些是lua完成?

由以上分析可看出,通过写lua可以改变已有操作原子的功能、参数,也可增加操作原子。但要通过这种方法修改操作原子时,须要足够了解lua以及lua可调用的、本游戏实现的C++库函数。
作者: ancientcc    时间: 2020-11-29 10:54
事件时机[filter][filter_second][filter_hero]
attack_end攻击后*1攻击单位防御单位--
last_breath攻击中出现一单位死亡*2死亡单位攻击单位--
moveto移动后*3移动的部队----
comeinto回城后*4回到的城市回城的部队--
post_duel单挑结束后*5左侧武将所在部队右侧武将所在部队--







欢迎光临 SDL中文论坛 (http://www.libsdl.cn/bbs/) Powered by Discuz! X3.3