【ERM】帮助、讨论和答疑帖
借鉴HC论坛,开一个ERM相关的讨主题。此主题讨论所有有关ERM的话题,各位可以跟帖提问或帮助解答,斑竹们可适当给解答的楼层一定的奖励。
这里并不是说不主张开新主题来提问ERM,集中起来提问解答可以快速处理很多问题,也能让一些学习ERM的玩家集中学习脚本。
感觉到学习ERM的玩家越老越少,希望有更多的接班人加入。
一句话:学习ERM并不是你想象中那么困难。
欢迎各位对ERM有兴趣的、有研究的、有疑问的、有奉献的、乐于助人的,都来这里留下你们ERM的足迹。
感觉这个主楼“空虚”了一点,列个有趣语法,看各位ERMer对以下脚本作何想法。当然,如果你能一眼看穿,那该是个资深ERMer了。
ZVSE
!?PI;
!!VRy1:S1010101 R1;
!!FUy1:P;
!!VRv1:S1010101 R1;
!!FUv1:P;
!!FU1010100:Pv1;
!?FU1010100;
!!FUx1|x1=1010101/x1=1010102:P;
!?FU1010101;
!!IF:M^1010101^;
!?FU1010102;
!!IF:M^1010102^;
本帖最后由 贤知有您 于 2014-10-16 14:26 编辑
【讨论】ERM执行顺序和ERM变量的应用
首先,如果你还不知道如何把ERM语句引入到游戏中,还不晓得什么叫触发器什么是接收器,请先阅读《ERM帮助》中关于“ERM教程”的部分。
建议学习或测试ERM时可以采用“以地图事件形式把ERM脚本引入游戏”。因为载入地图时只要不WOG化,就只执行地图事件的脚本了,可以排除其他脚本的干扰。
触发器是由游戏过程产生的各种触发事件,也就是让后续代码执行的前提条件。实际上触发器也是一个函数形式(!?开头)。所以,一般来说,某段代码开始执行时,应该是先从某个触发器代码开始的。
假设访问智慧树物体触发(!?OB102;)触发器,那么程序开始执行脚本中所有关于!?OB102;的函数主体部分(触发器或函数代码段姑且统称为函数主体)。
脚本文件(*.ERM以及事件ERM)之间的执行顺序,大致以这样理解:
1.地图事件ERM先执行,且事件的初发日越早越先执行.
2.S文件夹的ERM执行,如果不同MOD有相同的ERM脚本,只有最优先MOD的该脚本执行.
3.S文件夹下ERM文件的执行顺序比较奇特,并不完全由脚本的文件名排序顺序决定.
首先,如果是按照官方约定的命名方式"数字+空格+wog+空格+其他字符串.erm",那么前缀数字越大,越先执行.比如官方的<78 wog - wogify.erm>比<1 wog - cheat menu.erm>要早执行.
其次,"数字+空格"的组合几乎要比其它组合要早执行.
最后,没有空格情况下,看一组比较顺序(从左到右的执行顺序)
,.erm -> 0.erm -> 000.erm -> 1.erm -> 999.erm -> ab.erm -> bc.erm -> 我.erm
貌似有点让人捉摸不透,但至少我们可以自行测试一下便有分晓.
言归正传,所有脚本中带有(!?OB102;)触发器都会执行,而且是按上述ERM的顺序执行.
假设某个!?OB102;触发器代码是这样写的.
!?OB102;
!!FU12345:P;
!!VRv1:S0;
触发器(函数)下调用了另外一个函数FU12345.
那么程序又开始按照ERM文件处理顺序执行所有关于FU12345的函数主体(!?FU12345;)
只有当所有!?FU12345;函数执行完毕后,再回到上面那个OB102触发器,执行后面那句!!VRv1:S0;
只要调用了函数,都会如此这般执行,执行完毕后再回到原来的位置下一句继续执行.
当所有的!?OB102;主体都执行完毕了,也就是所有脚本都执行完毕,游戏继续进行,等待下一个触发器的产生.
为什么要花这么多文字来说明ERM语句执行顺序呢?后面将说明重要性.如何正确使用ERM的变量.
ERM变量类型包括:
1. 4字节的带符号整型变量:v变量,y变量,y-变量,f-t快捷变量,以及存在于函数内部的参数变量x1-x16
2. 1字节的标志变量: 1-1000
3. 4字节的特殊变量:w变量
4. 4字节的浮点数单精度变量:e变量,e-变量
5. 512字节的文本变量:z变量,z-变量
其中函数过程使用变量最多的是 v,y,y-和快捷变量.下面主要以这几个变量做说明.
y变量和y-变量(下标都是从1-100)被称为临时变量.
在旧版本的WOG中,y变量不能跨触发器,甚至不能跨函数来保存.意思就是说,当跳转到另外一个函数后,y变量的值会被重置为0.
写过脚本的都知道y变量可以跨函数返回时来使用.
虽然这样,但并不是说y变量可以随意跨度来用.(不同函数时会被清零)
这里涉及到一个变量存储期的问题,y变量的存储器很短,因为它经常被使用而容易冲突.
跨函数前y变量的值,跨函数返回时仍可获得这个值.很绕口,看下面一段代码.
你能直接猜出弹框的结果吗?
ZVSE
!?PI;
!!VRy1:S100;
!!IF:M^A Y1=%Y1^;
!!FU123:P;
!!IF:M^B Y1=%Y1^;
!?FU123;
!!IF:M^C Y1=%Y1^;
!!VRy1:S999;
!?FU123;
!!IF:M^D Y1=%Y1^;
!!VRy1:S-999;
!!FU124:P;
!!IF:M^E Y1=%Y1^;
!?FU124;
!!IF:M^F Y1=%Y1^;
结果可能出乎你意料,分别弹框是:
A Y1=100
C Y1=0
D Y1=999
F Y1=0
E Y1=-999
B Y1=100
怎么,拿来测试了吧,是不是越来越晕.这正正说明,临时变量的不确定性.
函数嵌套函数时,y临时变量最好少用,关键的变量最好使用v变量来处理.
说了这么久,怎么不说y-变量呢,其实y-变量很好用,在不对它赋值之前,它的值不变(跟v变量一样).
不过,ERA的作者bersy在ERA帮助中提到,不建议使用y-变量,可能是ERA的支持度还不够吧.
但官方脚本很多也用y-变量的,暂时还没发现什么问题,可以放心使用,但它仍属于临时变量.
至于快捷变量f-t, 跟y变量差不多,但要注意一个奇怪问题是,应用快捷变量的数字运算最好不带空格.
比如 !!VRy2:Si +y1; 提示是一个错误语法.不带空格的话!!VRy2:Si+y1;就没问题.(主要是加减方面会出BUG)
要长期保存值,当然首选v变量了.
你会发现,v变量仍然分成2部分,临时v变量和非临时v变量.临时v变量是从官方脚本中约定出来的.
临时v变量的好处是,可以跨函数使用,只要你确定没有被其它占用改变之前就可以一直保留.
而编写erm时也是不能跨触发器来使用v临时变量的.在触发器产生之前,它可能已经不是那样子了.
比如:
!?BG0;[动作前]
!!BG:N?v2 E?v3 Q?v4;
!?BG1;[动作后]
!!IF:M^攻击堆栈%V2 被攻击堆栈%V3 攻击方%V4^;
这种使用是不可靠的.BG0在这段没更改v2-v4,但可能下一个BG0就会改掉v2-v4.
就算所有BG0不更改v2-v4,中间可能触发!?MF1;等,仍然有可能被覆盖.
正确的方法,最好使用未曾用过的v变量.
可以研究一下沧海一粟大师的怪物强化,因为经常在其它触发器使用到BG0动作前的数据,所以沧海一粟使用了一整套v变量来记录,以便调用.
当然,你可以想象得到,这套v变量记录BG0信息(叫变量准备吧)需要在最前端的脚本先执行,后面才行应用.这也是脚本执行顺序的重要性.
这里顺带提醒一个特殊问题.
WOG官方脚本定义了很多宏变量,比如
!?TM2&$day$=1/$once$=1; // only once at day 1
应用起来很方便,但需要注意的是,假设你新建了一个erm文件 (100 wog - 我的脚本.erm)
在这个脚本中应用宏变量就会出错了.
宏变量作为预定义的部分,有点类似替换文本.比如上面的$once$会被事先替换为v2391.
由于100 wog 比78 wog - wogify.erm先被扫描,故会认为未定义宏变量而出错.
最后综合一下使用y和v变量注意事项:
1.y变量只能在某段顺序执行的函数代码下应用较好.尽量避免跨函数使用.
2.v变量的临时变量也应该在能预知不被改变的代码段中应用,跨函数时也要避免冲突.
3.跨函数可以直接使用x1-x15来传递一些特定值.比调用变量要方便.
关于Y变量在触发器之间的传递有些不是很理解的地方,想请007大人说得更明白一些~能不能帮忙解释下我用红字标出来的问题呢?谢谢。
或者说直接解释这个问题吧:Y变量的存储器很短 确切来说是存储多久?
ZVSE
!?PI;
!!VRy1:S100;
!!IF:M^A Y1=%Y1^;A Y1=100
!!FU123:P;
!!IF:M^B Y1=%Y1^; B Y1=100 这个似乎和E的结果又矛盾了……到底是怎么回事
!?FU123;
!!IF:M^C Y1=%Y1^; C Y1=0 这里Y1被清零了,是不是说明Y变量不能跨触发器?
!!VRy1:S999;
!?FU123;
!!IF:M^D Y1=%Y1^; D Y1=999 这里Y1没有被清零,是不是说明,即使是两段代码,但只要是同一个触发器,就可以传递Y变量?
!!VRy1:S-999;
!!FU124:P;
!!IF:M^E Y1=%Y1^; E Y1=-999 这里Y1=-999,说明!?FU124中的Y1值传递过来了?也就是说调用另一个!?FU是可以传递Y变量的?
!?FU124;
!!IF:M^F Y1=%Y1^; F Y1=0 这里的Y1到底是被清零了,还是上一个(999-999=0)的结果传递过来了?
反正我现在感到非常头疼,我一直以为Y变量就相当于函数内使用的局部变量,所以可以在每个触发器内安全地使用一套独立的Y变量,现在看了这个帖子以后感觉脑袋里一团浆糊……
!?PI;
!!VRy1:S1010101 R1;
!!FUy1:P;
!!VRv1:S1010101 R1;
!!FUv1:P;
!!FU1010100:Pv1;
能解释下这两条的效果有啥差别么……看了这个帖子以后我感到了自己的知识有多么不足…… 顺便再问个我以前遇到的小问题吧。
!?CO2&v7185=1;
!!HE-1:E?y-1;
!!CO-1:N?z-1;
!!CO-1:S0/1;
!!CO-1:S1/1;
!!CO-1:S2/1;
!!CO-1:S3/1;
!!CO-1:S4/1;
!!CO-1:S5/1;
!!CO-1:S6/1;
!!CO-1:A1/146/0;
!!CO-1:A1/147/0;
!!CO-1:A1/148/0;
!!CO-1:A1/150/0;
!!CO-1:A1/151/0;
!!CO-1:A1/154/0;
!!CO-1:X1/y-1;
!!CO-1:Nz-1;
这就是个很简单的宠物全技能罢了,当时遇到的问题是这两句红色的代码。如果不加这两句代码的话,那么宠物的名字会变成一个乱七八糟的字符串,例如“造成XXX~XXX伤害”之类。我当时也不明白原因,就这么简单包扎了一下。现在既然有专门的答疑帖了,又正好是在讨论变量的问题,就拿来请教下到底为什么会出现这种情况。
另外,看了007的帖子以后,决定把这里的Y-变量都换成Y变量…… wuxiangjinxing 发表于 2014-10-16 06:09
关于Y变量在触发器之间的传递有些不是很理解的地方,想请007大人说得更明白一些~能不能帮忙解释下我用红字 ...
呵呵,关于这个例子我一直珍藏.我也是从研究这些例子开始学ERM的.
Y变量可以在调用其它函数后返回调用前的值.这句话,我说起来都觉得很绕口.
我在例子上尽量说明一下.
之所以举例,并不是说要了解这么复杂的机制,而是说明,跨函数和触发器时最好不要用Y变量来长期存储数值.最少自己会被搞混乱的.
另外触发器很多时候都可以看成是函数.(在ERA下,普通触发器可以用专用FU函数写法代替)
ZVSE
!?PI;
!!VRy1:S100;
!!IF:M^A Y1=%Y1^;
!!FU123:P; [调用函数,此时Y1的值会被保护起来,我猜想Y1-Y100值是存入到其它地方了]
!!IF:M^B Y1=%Y1^;
!?FU123;
!!IF:M^C Y1=%Y1^;
!!VRy1:S999; [在这个FU123函数上,重新定义了Y=999]
!?FU123;
!!IF:M^D Y1=%Y1^;
!!VRy1:S-999; [这里FU123又重新定义了Y1=-999]
!!FU124:P; [跳转函数,Y1=-999被保护起来]
!!IF:M^E Y1=%Y1^;
!?FU124;
!!IF:M^F Y1=%Y1^;
我目前理解的情况是这样,欢迎有新见解的也来谈谈.
说起Y1值被保护起来,很多人可能会想,如果函数调用函数,被调用函数又调用函数,多次后,岂不是需要很多内存空间来存储(保护)这些Y变量?
我目前想到的机制是,使用内存堆栈处理的,压栈(PUSH)和弹出(POP).正如在ERA环境下,可以在每个函数中使用一套SN:X1/../x16; 充当变量一样. wuxiangjinxing 发表于 2014-10-16 06:11
!?PI;
!!VRy1:S1010101 R1;
!!FUy1:P;
差别就是,只有 v变量可以作为 FU的函数索引号.
也就是说,只有
!!VRv1:S1010101 R1;
!!FUv1:P;
能顺利执行,其它都不执行,但不会报错.
另外,由于宏变量可以替换v变量,所以宏变量在FU上也一样可用.
比如官方脚本
!!FU$LocalPlayer$:P; wuxiangjinxing 发表于 2014-10-16 07:05
顺便再问个我以前遇到的小问题吧。
!?CO2&v7185=1;
这个问题,我确实不甚理解,等待沧海大师来解答,他对指挥官命令比较熟悉.
感觉可能是内部的BUG.
@沧海一粟 贤知有您 发表于 2014-10-16 09:31
呵呵,关于这个例子我一直珍藏.我也是从研究这些例子开始学ERM的.
Y变量可以在调用其它函数后返回调用前 ...
多谢007的解释……不过看了以后,我的理解是:Y变量就是类似于一般程序语言的局部变量。
对每个触发器(可以理解为函数)而言,Y1~Y100都是独立的,只在这个触发器内有效。而在触发器开始时,这些变量都被赋值为0。
于是这段代码可以这么简单理解:
ZVSE
!?PI;
!!VRy1:S100;
!!IF:M^A Y1=%Y1^;A Y1=100
!!FU123:P;
!!IF:M^B Y1=%Y1^; B Y1=100 不管FU123怎么执行来执行去,都不会影响到该触发器内的Y变量(局部变量不会被其他函数访问到),所以Y1=100
!?FU123;
!!IF:M^C Y1=%Y1^; C Y1=0 这里的Y1是FU123的局部变量了,所以Y1初始为0
!!VRy1:S999;
!?FU123;
!!IF:M^D Y1=%Y1^; D Y1=999 这里实际上还是FU123,所以Y1=999
!!VRy1:S-999;
!!FU124:P;
!!IF:M^E Y1=%Y1^; E Y1=-999 同B,不管FU124怎么乱来,都不会影响到FU123的局部变量,所以Y1=-999
!?FU124;
!!IF:M^F Y1=%Y1^; F Y1=0 同C,Y1初始为0
所以,这段代码里并没有什么Y变量跨函数传递这种事情,并不能用来解释~#FU:C0; 到底起了什么作用……
wuxiangjinxing 发表于 2014-10-16 10:40
多谢007的解释……不过看了以后,我的理解是:Y变量就是类似于一般程序语言的局部变量。
对每个触发器 ...
要理解为 局部变量,效果上说得过去.
但实际上Y变量都是固定地址的.它应该是先保护,再清零设置,最后又还原.
而一般程序语言的局部变量并不是固定地址.相反是临时地址. 贤知有您 发表于 2014-10-16 12:22
要理解为 局部变量,效果上说得过去.
但实际上Y变量都是固定地址的.它应该是先保护,再清零设置,最后又还 ...
好像是这样没错,我又去看了下ERM帮助,发现了一点奇怪的地方:
以下摘自ERM 中文帮助 v2.81版
在触发器外使用y变量
有一组全局y变量。所有的函数FU触发器中的y变量都存储于同一个地方,在开始时都设为0,当使用这组全局y变量 时,可以在说明部分定义它,然后在每一个触发器中它们的初始值就是你所定义的值。需要注意的是,这个变量将会在函数触发器跳转时被清零。
举例:
ZVSE
!#VRy15:S3;
....
!?...;
!!IF:M^%Y15^; 显示 3
...
!!FU234:P;
!!IF:M^%Y15^; 显示 3
...
!?FU234;
!!IF:M^%Y15^; 显示 0
!!VRy15:S5;
!!IF:M^%Y15^; 显示 5
!?FU234;
!!IF:M^%Y15^; 显示 0
!!VRy15:S7;
!!IF:M^%Y15^; 显示 7
这个红色的是不是有问题? wuxiangjinxing 发表于 2014-10-16 12:47
好像是这样没错,我又去看了下ERM帮助,发现了一点奇怪的地方:
以下摘自ERM 中文帮助 v2.81版
最简单的方式,就是测试一下.实践检验真理.
目测是错误的,应该显示5,不是0.
ERM帮助部分内容有误的,最好还是亲自测试一下. 终于在源码中找到
~#FU:C0;
这句的用处.
实则上,它只是为了让y变量能在非函数外进行检测或设置.
所以2楼说的有关系是错误的,我编辑删掉了.
;cn; 我就弱弱的问一下,在封神MOD的情况下,怎么样可以使选择 指挥官强化时 死亡凝视可以不变成中毒。。。 本帖最后由 Archer30 于 2015-2-11 11:18 编辑
封神mod里用了5行来定义瑞恩的第二特长
!?PI;
!!FU40000:P8; #8瑞恩
!?FU40000&x1=8;
!!CO8:N?z1;
!!CO8:T1 Nz1 A1/149/0;
我不理解的是,这里引进FU40000和z1意义何在?这个片段跟
!?PI;
!!CO8:T1 A1/149/0;
又有什么区别?
请教下,嗜血奇术的动画编号是多少?就是BM:V#这的
另外,如何设置当回合士气或者幸运? blacksolar 发表于 2015-6-5 19:52
请教下,嗜血奇术的动画编号是多少?就是BM:V#这的
另外,如何设置当回合士气或者幸运?
嗜血奇术并非动画来实现的,是内部依照DEF重新描绘做出的,暂时无法模拟出来.
设置当前回合士气高涨或幸运一击需要用到内存修改.如果你不了解内存修改需要注意的事项,恐怕会引起和其它脚本的冲突.
关于士气高涨改法,可以研究一下以下脚本.
运气那个我一时找不到了.懒得再翻看内存,等沧海回来他肯定可以给你列出.
!?BG0&1000;
!!BG:A?y1 N?y2;
!!FU&y1<>6/y1<>7/y1<>2/y1<>10:E;[移动攻击,射击,移动和施法]
!!FU|y2<0:E;
!!BMy2:T?y3 N?y4;
!!FU|y3<0/y3=149/y4<1:E;[排除箭塔]并没有指定生物,也就应用与任意堆栈
!!UN:C4605354/4/1;[士气高涨几率改为1->1,只要士气值>0,必出]
!!SN:W^007.ChangeGoodMorale^/111;[更改了内存的标志]
!?BG1&1000;[动作后还原内存]
!!SN:W^007.ChangeGoodMorale^/?y99;
!!FU&y99<>111:E;
!!SN:W^007.ChangeGoodMorale^/0;
!!UN:C4605354/4/24;[原几率是1->24] 贤知有您 发表于 2015-6-5 21:31
嗜血奇术并非动画来实现的,是内部依照DEF重新描绘做出的,暂时无法模拟出来.
设置当前回合士气高涨或幸 ...
多谢哈,,
有个地方有些疑惑
!!UN:C4605354/4/1;[士气高涨几率改为1->1,只要士气值>0,必出]
这里注释,>0必出,为什么最后恢复内存的时候又改成24?是说每次初始的时候是24,然后在BG0的时候重新计算修改?那么其实不恢复也应该问题不大吧?。
我发现士气是在BG0后,BG1之前发生的。看你的代码,还不是每个堆栈单独一个值,所以BM:F,0x01000000 = 16777216 - 当前堆栈士气向上,这个应该是只读、设置无效的吧? 贤知有您 发表于 2015-6-5 21:31
嗜血奇术并非动画来实现的,是内部依照DEF重新描绘做出的,暂时无法模拟出来.
设置当前回合士气高涨或幸 ...
还有一个,自定义变量,性质属于v变量——可以跨触发器应用? blacksolar 发表于 2015-6-6 00:05
多谢哈,,
有个地方有些疑惑
!!UN:C4605354/4/1;[士气高涨几率改为1->1,只要士气值>0,必出]
这个24就是内存上本身的值.
如果你有研究H3,或者看看宝典,就知道,士气的概率 1/24 2/24 3/24 分别对应1点-3点士气的情况(最多3点,分正负)
士气高涨实则是在BG0之后,完成堆栈动作后出现的,在BG1触发器之前.
这里提前修改了内存编码,让这个带正士气的情况变得100%能进行.
BG1后必须修改,否则游戏就变质了.(后面所有正士气堆栈都必会士气高涨)
至于你提到的堆栈士气高涨的标志,这个标志是为了确保不会出现二次士气高涨的情况.也就是说,它出现在士气高涨判断完成后.
如果你前置设置这个标志(BG0设置),那么这个堆栈就不会再出现士气高涨了.
V变量可以任意使用,跟触发器无关,除非有其它语句更改了这个值.它是全局变量.
当然V变量也有分临时和非临时,查看ERM帮助能了解到.
不管是否临时,它都只在被重新赋值时才变化.