
标题: DOS版三国志英杰传的研究心得——伍 [打印本页]
作者:
漫漫苦短 时间: 2025-8-9 19:19 标题: DOS版三国志英杰传的研究心得——伍
从前面几章的介绍来看,道具的数据也是有在内存中占有一块的数据空间,而且与人物的数据也是有联系的,这块数据究竟有多大,这个就是本章探索的问题。
不过说起道具,相信很多玩家想起英杰传的经典介绍。
《三国志英杰传》是KOEI出品的一个集战略模拟与角色扮演于一身的上乘之作。其故事与“三国”原著颇有关联,而且免去了繁琐的练兵、种田,并提供64种道具及30余种策略,让玩者在战场上杀得淋漓尽致。
虽然听上去64个道具很吸引眼球,不过实际上这似乎是个障眼法,有很多道具具有相似的功能,那么第一节就开始从道具的简介来了解英杰传的道具,然后再由已有的知识,结合道具的内存空间以及相关代码,获得更多道具相关的细节。
目录- 一、道具的简介
- 二、道具数据的组成
- 三、道具价格的计算(买与卖)
- 四、道具的分类(1)
- 五、道具的分类(2)
- 六、道具的分类(3)
- 七、道具的使用(01變換用类)(1)
- 八、道具的使用(01變換用类)(2)
- 九、道具的使用(01變換用类)(3)
- 十、道具的使用(02攻擊用类)(1)
- 十一、道具的使用(02攻擊用类)(2)
- 预告 十二、道具的使用(02攻擊用类)(3)
[ 本帖最后由 漫漫苦短 于 2026-3-31 20:39 编辑 ]
作者:
漫漫苦短 时间: 2025-8-19 19:19 标题: 一、道具的简介
对于这64个道具,根据已有的知识,可以先将它们分个几类,比如增加攻击力的武器:青龍偃月刀、蛇矛、方天畫戟等等;增加防御力的兵書:孫子兵法、吳子兵法、孟德新書等等;增加移动力的馬:赤兔馬、的盧、黃爪飛龍(没错,游戏中确实是叫这个名字);在每个行动回合开始自动恢復的道具:玉璽、赦命書、援軍報告;剩下的都是主动类道具(我军在行动回合操作使用的),并且是一次性道具,然后其实还可以再对这些道具进行一次再次分类,第一类只能对道具持有者使用并且有兵种使用限制的,比如有升职的,長槍、步兵車、連弩、發石車、馬鎧、近衛鎧、無賴精神、俠義精神,转职的,遁甲天書、青囊書、鼓吹具、弓術指南書、馬術指南書、劍術指南書。第二类只能对我军或友军使用的恢復用的道具,有必须指向的单体道具,酒、特級酒、老酒分别是三阶士气回复道具;豆、麥、米分别是三阶兵力回复道具;傷藥、中藥、茶分别是三阶士气兵力回复道具;有以使用者为中心的群体道具,平氣書、活氣書、勇氣書分别是三阶群体士气回复道具;援隊書、援部書、援軍書分别是三阶群体兵力回复道具。第三类只能对敌军使用的道具,濃霧書、雷陣雨書、豪雨書都是可以混乱敌军的,剩下的都是伤害类道具,焦熱書、火龍書、猛火書分别是三阶火系伤害道具;漩渦書、濁流書、海嘯書分别是三阶水系伤害道具;落石書、山崩書、山洪書分别是三阶石系伤害道具;最后还剩一个伤害道具炸弹,它是没有使用地形限制,然后伤害的算法也不太一样,算是特殊的道具。
根据上面的简要分析,先预估一个道具究竟需要多少字节来存储信息,首先需要1个字节来存储道具编号,64个道具对应16进制就是00至3F,还需要存储道具的名字,最长的道具有5个字,加上字符串末尾的0标志,就是11个字节,然后上述的道具分类,也需要1个字节存储,然后每个分类中每个道具的额外数值不一样,例如像攻击力的武器、防御力的兵書、移动力的馬的分类中可以有不同加成,升职和转职道具要记录变换的兵种和使用限制的兵种,这些也可能要占用几个字节,还有道具的黄金价格,最大的买道具的价格为2000(援軍書),最大的卖道具的价格为1910(不可买道具),但根据周瑜前辈的英杰传详细流程攻略,卖的道具可以由买的道具计算得出:
道具卖价是买价的75%,个位数直接舍去。
可以用2个字节记录买的价格或卖的价格,不过还有另一种可能性可能这个数据已经包含在前面的额外数值里。
那么实际上一个道具究竟在内存空间中占用多大的内存空间,每个字节究竟有什么作用,本章开始逐步分析。
作者:
漫漫苦短 时间: 2025-9-9 19:19 标题: 二、道具数据的组成
上节的简介已经将道具的现有知识,不过还有一部分疏漏,每个道具其实有一串比较长的文字叙述,这样其实每个道具占用的内存是比上节最后分析的内容要多。
如果用过龙吟或godtype以及其他作者的修改器,就会发现一个问题,如果要修改道具的名字属性等等,会修改BAKDATA.R3文件,但是如果改动道具的介绍的部分,则会改变MAIN.EXE的内容。上节讨论的道具的名字属性等等,也是程序通过读取BAKDATA.R3文件中道具的数据,并导入到内存空间中。而如果从内存分段的角度来说,这两部分都在DS段中,只是有部分数据是来自MAIN.EXE,可以称为内部数据,而来自BAKDATA.R3文件中的数据,可以称为外部数据,以下就简要分析外部数据的部分。
每个道具所占的(外部)数据共17个字节,对应的16进制的偏移就是00-10,但其实只有以下5个部分:
偏移+00 占n字节 道具名字
偏移+0D 占1字节 道具代码
偏移+0E 占1字节 道具购买黄金价格
偏移+0F 占1字节 道具特殊作用额外数值
偏移+10 占1字节 道具特殊作用
其中道具的名字占了13个字节的空间,但不是每个字节空间都被占用了,因为名字的长度不一样,但为了每个道具所占用的空间一样以及为了数据对齐的目的,这样就可以更加方便找到每个道具数据数据的首地址,强制占用了这些空间。一个汉字对应这2个字节的空间,这样看来可以把道具的名字最大改成为六个字的道具。
然后偏移+0E+0F+10就是本章将要讲述的重点,与上节的分析不太一致的地方就是道具购买黄金价格,这里只用了1个字节来记录,那么1个字节是如何记录道具购买黄金价格,下一节就开始分析一下。
[ 本帖最后由 漫漫苦短 于 2025-9-9 20:05 编辑 ]
作者:
漫漫苦短 时间: 2025-9-19 19:19 标题: 三、道具价格的计算(买与卖)
上一节提到了道具数据中只用了1个字节来记录购买黄金价格,如果过目了周瑜的英杰传详细流程攻略中的道具价格,就会看出所有的道具买与卖价格都是10的倍数,那么自然而然就有一个想法,像第肆章介绍的兵种基本兵力和兵种每等级兵力增幅,道具数据记录的购买黄金价格乘10就是游戏中看到的道具买的价格。
计算道具买的价格的代码,其中AX为道具的代码。
seg003:2041 6B F0 11 imul si, ax, 11h
seg003:2044 81 C6 1C BE add si, 0BE1Ch
seg003:2048 56 push si
seg003:2049 9A 3C 27 F0 3C call sub_3F63C
seg003:204E 2A E4 sub ah, ah
seg003:2050 6B F8 0A imul di, ax, 0Ah
计算道具卖的价格的代码,其中AX为道具的代码。
seg003:2532 6B F8 11 imul di, ax, 11h
seg003:2535 81 C7 1C BE add di, 0BE1Ch
seg003:2539 57 push di
seg003:253A 9A 3C 27 F0 3C call sub_3F63C
seg003:253F B1 03 mov cl, 3
seg003:2541 F6 E1 mul cl
seg003:2543 C1 E8 02 shr ax, 2
seg003:2546 6B F0 0A imul si, ax, 0Ah
可以看到都调用了sub_3F63C这个函数。
seg003:273C sub_3F63C proc far ; CODE XREF: sub_3EEB6+93↑P ; sub_3F29A+1A0↑P
seg003:273C
seg003:273C arg_0 = word ptr 6
seg003:273C
seg003:273C 55 push bp
seg003:273D 8B EC mov bp, sp
seg003:273F 8B 5E 06 mov bx, [bp+arg_0]
seg003:2742 8A 47 0E mov al, [bx+0Eh]
seg003:2745 C9 leave
seg003:2746 CA 02 00 retf 2
seg003:2746 sub_3F63C endp
买的部分的代码很简单,就是获取道具数据的偏移+0E的1字节保存在AL寄存器中,再乘10就是最终的价格,卖的部分的代码就需要注意一下计算的次序了。
作者:
漫漫苦短 时间: 2025-11-9 19:19 标题: 四、道具的分类(1)
这一节简单介绍道具特殊作用的分类,第一节的简单给道具作了分类,但这个分类只是根据我们在游戏中的经验作出的分类,而游戏中实际上对道具分了6大类。
dseg:3FE8 AA 5A BE B9 00 asc_447B8 text "BIG5", '武器',0 ; DATA XREF: dseg:400A↓o
dseg:3FED C5 DC B4 AB A5 CE 00 asc_447BD text "BIG5", '變換用',0 ; DATA XREF: dseg:400C↓o
dseg:3FF4 A7 F0 C0 BB A5 CE 00 asc_447C4 text "BIG5", '攻擊用',0 ; DATA XREF: dseg:400E↓o
dseg:3FFB AB EC B4 5F A5 CE 00 asc_447CB text "BIG5", '恢復用',0 ; DATA XREF: dseg:4010↓o
dseg:4002 B0 A8 00 asc_447D2 text "BIG5", '馬',0 ; DATA XREF: dseg:4012↓o
dseg:4005 A7 4C AE D1 00 asc_447D5 text "BIG5", '兵書',0 ; DATA XREF: dseg:4014↓o
dseg:400A E8 3F dw offset asc_447B8 ; "武器"
dseg:400C ED 3F dw offset asc_447BD ; "變換用"
dseg:400E F4 3F dw offset asc_447C4 ; "攻擊用"
dseg:4010 FB 3F dw offset asc_447CB ; "恢復用"
dseg:4012 02 40 dw offset asc_447D2 ; "馬"
dseg:4014 05 40 dw offset asc_447D5 ; "兵書"
也就是说前文介绍的“偏移+10 占1字节 道具特殊作用”只有6个值,00武器,01變換用,02攻擊用,03恢復用,04馬,05兵書,可以看到有一部分的猜想是准确的:
比如增加攻击力的武器:青龍偃月刀、蛇矛、方天畫戟等等;增加防御力的兵書:孫子兵法、吳子兵法、孟德新書等等;增加移动力的馬:赤兔馬、的盧、黃爪飛龍(没错,游戏中确实是叫这个名字);剩下的都是主动类道具(我军在行动回合操作使用的),并且是一次性道具
但玉璽、赦命書、援軍報告这3个道具却被归类到“恢復用”道具,而除了这3个,其他也被归类到“恢復用”道具例如酒、豆都是主动类一次性道具。
对于00武器,04馬,05兵書这三类道具,可以通过修改BAKDATA.R3,不仅能修改具体的额外数据,也能修改其分类,例如把00武器修改为04馬。
作者:
漫漫苦短 时间: 2025-11-19 19:19 标题: 五、道具的分类(2)
上一节介绍了游戏中实际上道具分的6大类,这一节对其中的一类"馬",也就是涉及部队移动力的道具介绍一下它所起的作用,关于移动力,曾经在五、人物数据的重要属性——兵种(4)有所介绍,当时还留了一部分代码没有介绍,此处正好将其补充介绍。
sub_33D26函数是计算部队移动力的方法,arg_0为部队战场数据的首地址。
seg002:6E26-seg002:6E6B是计算道具带来的移动力加成的计算并且保存到var_4中,由于涉及到道具的相关知识,本处省略其中的计算过程。
seg002:6E26 32 C0 xor al, al
seg002:6E28 88 46 FC mov [bp+var_4], al
seg002:6E2B 88 46 FE mov [bp+var_2], al
seg002:6E2E loc_33D4E: ; CODE XREF: sub_33D26+65↓j
seg002:6E2E 8A 46 FE mov al, [bp+var_2]
seg002:6E31 50 push ax
seg002:6E32 56 push si
seg002:6E33 9A DE C5 F6 1C call sub_2953E ; 获取部队第ax个道具的代码
seg002:6E38 88 46 FF mov [bp+var_1], al
seg002:6E3B 3C FF cmp al, 0FFh
seg002:6E3D 74 25 jz short loc_33D84
seg002:6E3F B0 11 mov al, 11h
seg002:6E41 F6 66 FF mul [bp+var_1]
seg002:6E44 05 1C BE add ax, 0BE1Ch
seg002:6E47 89 46 F6 mov [bp+var_A], ax
seg002:6E4A 50 push ax
seg002:6E4B 9A 68 6B F6 1C call sub_23AC8 ; 获取道具特殊作用
seg002:6E50 3C 04 cmp al, 4 ; 判断是不是04馬
seg002:6E52 75 10 jnz short loc_33D84
seg002:6E54 FF 76 F6 push [bp+var_A]
seg002:6E57 9A 5A 6B F6 1C call sub_23ABA ; 获取道具特殊作用额外数值
seg002:6E5C 3A 46 FC cmp al, [bp+var_4]
seg002:6E5F 76 03 jbe short loc_33D84
seg002:6E61 88 46 FC mov [bp+var_4], al ; 修改道具特殊作用额外数值最大值
seg002:6E64 loc_33D84:
seg002:6E64 FE 46 FE inc [bp+var_2]
seg002:6E67 80 7E FE 08 cmp [bp+var_2], 8
seg002:6E6B 72 C1 jb short loc_33D4E
这段代码有4个变量,var_1是道具代码,var_2是某个人物的道具编号,var_4是道具特殊作用额外数值最大值,var_A是根据道具代码计算出的道具数据的首地址。
代码的逻辑也不复杂,就是循环8个道具,seg002:6E3B先判断是不是空道具(FF),seg002:6E50判断这个道具是不是04馬,如果是04馬,就取出其特殊作用额外数值,seg002:6E5C将该数值与var_4比较,如果小于等于则跳转,否则在seg002:6E61修改var_4为该道具特殊作用额外数值。这也就保证了一个人物拥有赤兔马(+3移动力)或的卢(+2移动力),无论道具编号顺序是赤兔马在前或的卢在前,最后计算出的移动力加成都是3,而不是2或5。
从这段代码可以看到,一个道具是否具有能给部队增加移动力的功能,只与他的道具特殊作用(偏移+10 占1字节)是否为04馬,并不是与它的名称,买卖价格等属性有关,而它的特殊作用额外数值(偏移+0F 占1字节)也是只与这1个字节的数值有关。
同样也可以拓展一下,在未做代码修改的情况下,对于所有的道具,在00武器、04馬、05兵書三个额外作用中,他仅能拥有这三个额外作用中的一个,例如如果将赤兔马的特殊作用(偏移+10 占1字节)的值从04修改为05,那么这个赤兔马就不再具有馬(移动力加成)的功能,而是具有兵書(防御加成)即防御加成3%;而将霸王之剑的特殊作用的值从00修改为04,那么霸王之剑不再具有武器(攻击力加成)的功能,而是具有馬(移动力加成)即+24移动力,霸王之剑虽不再加成攻击力,但想去哪儿就去哪儿,赤兔马也只能望尘莫及。
[ 本帖最后由 漫漫苦短 于 2025-11-19 20:09 编辑 ]
作者:
漫漫苦短 时间: 2025-11-29 19:19 标题: 六、道具的分类(3)
上一节提到了,对于00武器、04馬、05兵書三种道具,是可以通过修改道具特殊作用的数值实现道具作用的修改,那么也就基本可以猜到,在攻击力或防御力的计算的相关代码中,计算道具的最大攻击加成或防御加成,与获取道具最大移动力的代码,应该是很类似的,把计算道具的最大攻击加成和防御加成放在下面:
seg001:6083 32 C0 xor al, al
seg001:6085 88 46 F9 mov [bp+var_7], al
seg001:6088 88 46 FF mov [bp+var_1], al
seg001:608B loc_22FEB: ; CODE XREF: sub_22F6A+C6↓j
seg001:608B 8A 5E FF mov bl, [bp+var_1]
seg001:608E 2A FF sub bh, bh
seg001:6090 03 DE add bx, si
seg001:6092 83 C3 23 add bx, 23h
seg001:6095 89 5E F6 mov [bp+var_A], bx
seg001:6098 80 3F FF cmp byte ptr [bx], 0FFh ; 判断道具代码是否FF(空)
seg001:609B 74 2C jz short loc_23029
seg001:609D B0 11 mov al, 11h
seg001:609F F6 27 mul byte ptr [bx]
seg001:60A1 05 1C BE add ax, 0BE1Ch
seg001:60A4 50 push ax
seg001:60A5 9A 68 6B F6 1C call sub_23AC8 ; 获取道具特殊作用
seg001:60AA 0A C0 or al, al ; 判断是否是攻击加成道具
seg001:60AC 75 1B jnz short loc_23029
seg001:60AE B0 11 mov al, 11h
seg001:60B0 8B 5E F6 mov bx, [bp+var_A]
seg001:60B3 F6 27 mul byte ptr [bx]
seg001:60B5 05 1C BE add ax, 0BE1Ch
seg001:60B8 50 push ax
seg001:60B9 9A 5A 6B F6 1C call sub_23ABA ; 获取道具特殊作用额外数值(攻击加成)
seg001:60BE 88 46 FE mov [bp+var_2], al
seg001:60C1 3A 46 F9 cmp al, [bp+var_7] ; 比较攻击加成是否超过最高攻击加成
seg001:60C4 76 03 jbe short loc_23029
seg001:60C6 88 46 F9 mov [bp+var_7], al ; 超过最高攻击加成,替换
seg001:60C9
seg001:60C9 loc_23029: ; CODE XREF: ...
seg001:60C9 FE 46 FF inc [bp+var_1]
seg001:60CC 80 7E FF 08 cmp [bp+var_1], 8
seg001:60D0 72 B9 jb short loc_22FEB
seg001:61B1 32 C0 xor al, al
seg001:61B3 88 46 F9 mov [bp+var_7], al
seg001:61B6 88 46 FF mov [bp+var_1], al
seg001:61B9 loc_23119: ; CODE XREF: sub_23098+C6↓j
seg001:61B9 8A 5E FF mov bl, [bp+var_1]
seg001:61BC 2A FF sub bh, bh
seg001:61BE 03 DE add bx, si
seg001:61C0 83 C3 23 add bx, 23h
seg001:61C3 89 5E F6 mov [bp+var_A], bx
seg001:61C6 80 3F FF cmp byte ptr [bx], 0FFh ; 判断道具代码是否FF(空)
seg001:61C9 74 2C jz short loc_23157
seg001:61CB B0 11 mov al, 11h
seg001:61CD F6 27 mul byte ptr [bx]
seg001:61CF 05 1C BE add ax, 0BE1Ch
seg001:61D2 50 push ax
seg001:61D3 9A 68 6B F6 1C call sub_23AC8 ; 获取道具特殊作用
seg001:61D8 3C 05 cmp al, 5 ; 判断是否是防御加成道具
seg001:61DA 75 1B jnz short loc_23157
seg001:61DC B0 11 mov al, 11h
seg001:61DE 8B 5E F6 mov bx, [bp+var_A]
seg001:61E1 F6 27 mul byte ptr [bx]
seg001:61E3 05 1C BE add ax, 0BE1Ch ; 道具固有数据的首地址
seg001:61E6 50 push ax
seg001:61E7 9A 5A 6B F6 1C call sub_23ABA ; 获取道具特殊作用额外数值(攻击加成)
seg001:61EC 88 46 FE mov [bp+var_2], al
seg001:61EF 3A 46 F9 cmp al, [bp+var_7] ; 判断防御加成是否超过最高防御加成
seg001:61F2 76 03 jbe short loc_23157
seg001:61F4 88 46 F9 mov [bp+var_7], al ; 超过最高防御加成,替换
seg001:61F7 loc_23157: ; CODE XREF: ...
seg001:61F7 FE 46 FF inc [bp+var_1]
seg001:61FA 80 7E FF 08 cmp [bp+var_1], 8
seg001:61FE 72 B9 jb short loc_23119
可以看到,计算道具的最大攻击加成和防御加成的代码非常类似,方法也十分类似,都是循环8个道具,判断是否道具特殊作用是00武器、05兵書,从中取出最大的数值。
那么关于道具6个分类中的3个分类00武器、04馬、05兵書都已经简单介绍了,还有3个分类01變換用、02攻擊用、03恢復用,可以看到与前者不同的地方在于,这3个分类的武器大部分都是主动使用道具(08玉璽、37赦命書、38援軍報告除外),那么主动使用的道具又是如何发挥作用的,这将是本章将要详细介绍的部分。
[ 本帖最后由 漫漫苦短 于 2025-11-29 20:19 编辑 ]
作者:
漫漫苦短 时间: 2025-12-19 19:19 标题: 七、道具的使用(1)
此前似乎一直没有介绍过一些相当复杂的函数,而道具的使用这个方法函数sub_36C9C可能是目前遇到的第一个较复杂且需要全面分析的函数,虽然只有sub_369F8函数会调用它,当一只部队选好道具并使用的时候就会调用,代码如下,其中ax即sub_36C9C函数中的arg_2为道具代码,si即sub_36C9C函数中的arg_0为部队的战场数据首地址。
seg002:9BEA 50 push ax
seg002:9BEB 56 push si
seg002:9BEC 9A 7C 9D F2 2C call sub_36C9C
接着进入sub_36C9C函数中,先来函数开头的部分,从seg002:9D93-9DBA是连续的判断。
seg002:9D7C sub_36C9C proc far ; CODE XREF: sub_369F8+114↑P
seg002:9D7C
seg002:9D7C arg_0= word ptr 6
seg002:9D7C arg_2= word ptr 8
seg002:9D7C
seg002:9D7C C8 2A 00 00 enter 2Ah, 0
seg002:9D80 57 push di
seg002:9D81 56 push si
seg002:9D82 8B 76 06 mov si, [bp+arg_0]
seg002:9D85 6B 04 2C imul ax, [si], 44
seg002:9D88 05 16 68 add ax, 6816h
seg002:9D8B 89 46 EE mov [bp+var_12], ax
seg002:9D8E C7 46 F4 00 00 mov word ptr [bp+var_C], 0
seg002:9D93 80 7E 08 03 cmp byte ptr [bp+arg_2], 3
seg002:9D97 73 03 jnb short loc_36CBC ; 无符号不小于跳转
seg002:9D99 E9 D7 05 jmp loc_37293
seg002:9D9C loc_36CBC: ; CODE XREF: sub_36C9C+1B↑j
seg002:9D9C 80 7E 08 10 cmp byte ptr [bp+arg_2], 10h
seg002:9DA0 72 09 jb short loc_36CCB ; 无符号小于跳转
seg002:9DA2 80 7E 08 12 cmp byte ptr [bp+arg_2], 12h
seg002:9DA6 77 03 ja short loc_36CCB ; 无符号大于跳转
seg002:9DA8 E9 C8 05 jmp loc_37293
seg002:9DAB loc_36CCB: ; CODE XREF: sub_36C9C+24↑j ; sub_36C9C+2A↑j
seg002:9DAB 80 7E 08 13 cmp byte ptr [bp+arg_2], 13h
seg002:9DAF 73 03 jnb short loc_36CD4 ; 无符号不小于跳转
seg002:9DB1 E9 01 01 jmp loc_36DD5
seg002:9DB4 loc_36CD4: ; CODE XREF: sub_36C9C+33↑j
seg002:9DB4 80 7E 08 1A cmp byte ptr [bp+arg_2], 1Ah
seg002:9DB8 76 03 jbe short loc_36CDD
seg002:9DBA E9 F8 00 jmp loc_36DD5
在seg002:9D93的判断中由于无符号判断arg_2的值肯定非负数,因此道具代码为00-02才转到seg002:9D99处,seg002:9D9C和seg002:9DA2进行两次判断,道具代码小于10和大于12都跳转到seg002:9DAB处,而道具代码为10-12才转到seg002:9DA8处,在执行完seg002:9D99或seg002:9DA8这行代码后,会共同跳到loc_37293处。
现在经过几条较简单的判断后,代码已经将01變換用这类道具摘出来了,00遁甲天書 01青囊書 02鼓吹具 10弓術指南書 11馬術指南書 12劍術指南書这6个转职书会跳到loc_37293继续执行代码,13長槍 14步兵車 15連弩 16發石車 17馬鎧 18近衛鎧 19無賴精神 1A俠義精神这8个升阶道具会跳到loc_36CDD(也就是seg002:9DBA的下一行),而其他道具会继续到loc_36DD5处进行更多的判断。
比起00武器、04馬、05兵書三种道具的判断简洁明了,这些道具仅仅判断的角度而言似乎有些复杂,如果没有事先了解道具代码以及对应的道具名称和特点,是很难直观看出这段代码的作用。
显然为了实现将道具的各自作用,会按照道具代码将道具进行分块处理,通过分析每一块的分块处理,也可以判断出每一个道具的具体作用。
下一节继续分析01變換用的道具是如何实现部队转职的。
[ 本帖最后由 漫漫苦短 于 2026-1-29 19:21 编辑 ]
作者:
漫漫苦短 时间: 2026-1-19 19:19 标题: 八、道具的使用(2)
seg002:A373 loc_37293: ; CODE XREF: sub_36C9C+1D↑j ; sub_36C9C+2C↑j
seg002:A373 C6 46 F8 0F mov [bp+var_8], 0Fh ; 遁甲天書-妖术师
seg002:A377 C6 46 F9 12 mov [bp+var_7], 12h ; 青囊書-运输队
seg002:A37B C6 46 FA 0C mov [bp+var_6], 0Ch ; 鼓吹具-军乐队
seg002:A37F C6 46 FB 03 mov [bp+var_5], 3 ; 弓術指南書-弓兵
seg002:A383 C6 46 FC 06 mov [bp+var_4], 6 ; 馬術指南書-轻骑兵
seg002:A387 C6 46 FD 00 mov [bp+var_3], 0 ; 劍術指南書-短兵
seg002:A38B 80 7E 08 10 cmp byte ptr [bp+arg_2], 10h
seg002:A38F 72 07 jb short loc_372B8
seg002:A391 8A 46 08 mov al, byte ptr [bp+arg_2]
seg002:A394 2C 0D sub al, 0Dh
seg002:A396 EB 03 jmp short loc_372BB
seg002:A398 loc_372B8: ; CODE XREF: sub_36C9C+613↑j
seg002:A398 8A 46 08 mov al, byte ptr [bp+arg_2]
seg002:A39B
seg002:A39B loc_372BB: ; CODE XREF: sub_36C9C+61A↑j
seg002:A39B 88 46 FF mov [bp+var_1], al
seg002:A39E 8B DE mov bx, si ; 部队战场数据的首地址
seg002:A3A0 83 3F 00 cmp word ptr [bx], 0 ; 判断是不是刘备
seg002:A3A3 75 03 jnz short loc_372C8
seg002:A3A5 E9 E7 00 jmp loc_373AF
seg002:A3A8 loc_372C8: ; CODE XREF: sub_36C9C+627↑j
seg002:A3A8 56 push si ; 部队战场数据的首地址
seg002:A3A9 9A 6C 93 F2 2C call sub_3628C ; 获取部队兵种代码
seg002:A3AE 88 46 FE mov [bp+var_2], al
seg002:A3B1 FF 76 06 push [bp+_部队战场数据的首地址_arg_0]
seg002:A3B4 9A 6C 93 F2 2C call sub_3628C
seg002:A3B9 8B 76 FF mov si, word ptr [bp+var_1]
seg002:A3BC 81 E6 FF 00 and si, 0FFh
seg002:A3C0 8A 4A F8 mov cl, [bp+si-8]
seg002:A3C3 88 4E DA mov [bp+var_26], cl ; 转职的兵种
seg002:A3C6 3A C8 cmp cl, al ; 判断是不是同一兵种
seg002:A3C8 75 03 jnz short loc_372ED
seg002:A3CA E9 BC 00 jmp loc_373A9
作者:
漫漫苦短 时间: 2026-2-9 19:19 标题: 九、道具的使用(01變換用类)(3)
seg002:9DBD loc_36CDD: ; CODE XREF: sub_36C9C+3C↑j
seg002:9DBD C6 46 F6 00 mov [bp+var_A], 0 ; 長槍-短兵
seg002:9DC1 C6 46 F7 01 mov [bp+var_9], 1 ; 步兵車-长兵
seg002:9DC5 C6 46 F8 03 mov [bp+var_8], 3 ; 連弩-弓兵
seg002:9DC9 C6 46 F9 04 mov [bp+var_7], 4 ; 發石車-连弩兵
seg002:9DCD C6 46 FA 06 mov [bp+var_6], 6 ; 馬鎧-轻骑兵
seg002:9DD1 C6 46 FB 07 mov [bp+var_5], 7 ; 近衛鎧-重骑兵
seg002:9DD5 C6 46 FC 09 mov [bp+var_4], 9 ; 無賴精神-山贼
seg002:9DD9 C6 46 FD 0A mov [bp+var_3], 0Ah ; 俠義精神-恶贼
seg002:9DDD B0 0F mov al, 15
seg002:9DDF 88 46 E6 mov [bp+var_1A], al ; 長槍
seg002:9DE2 88 46 E8 mov [bp+var_18], al ; 連弩
seg002:9DE5 88 46 EA mov [bp+var_16], al ; 馬鎧
seg002:9DE8 88 46 EC mov [bp+var_14], al ; 無賴精神
seg002:9DEB B0 1E mov al, 30
seg002:9DED 88 46 E7 mov [bp+var_19], al ; 步兵車
seg002:9DF0 88 46 E9 mov [bp+var_17], al ; 發石車
seg002:9DF3 88 46 EB mov [bp+var_15], al ; 近衛鎧
seg002:9DF6 88 46 ED mov [bp+var_13], al ; 俠義精神
可以看到seg002:9DBD-9DF6都是一段非常有规律的代码,作用就是对着一堆var变量设置值,虽然可以一眼就看出其中的写法规律,但分析其中的实际作用还得看后续的代码。
seg002:9DF9 8A 46 08 mov al, byte ptr [bp+arg_2]
seg002:9DFC 2C 13 sub al, 13h
seg002:9DFE 88 46 FF mov [bp+var_1], al
seg002:9E01 56 push si
seg002:9E02 9A 6C 93 F2 2C call sub_3628C ; 获取部队兵种代码
seg002:9E07 8A 4E FF mov cl, [bp+var_1]
seg002:9E0A 2A ED sub ch, ch
seg002:9E0C 8B F9 mov di, cx
seg002:9E0E 38 43 F6 cmp [bp+di+var_A], al ; 判断是否是对应兵种
seg002:9E11 74 03 jz short loc_36D36
seg002:9E13 E9 98 00 jmp loc_36DCE ; 不是对应兵种
这段代码初看会觉得这里花里胡哨的。为什么seg002:9DFC要将部队兵种代码减去13h?为什么要把这个值先保存到var_1并且最后再传到di中,以及最后的seg002:9E0E为什么能实现兵种间的比较。
首先兵种代码保存在di中属于无奈之举,由于汇编代码的机器码指令有限,换句话说就是不能像高级语言编程一样可以各种定义变量以及实现各种复杂书写方式,所以只能实现一种特定的比较方式,在seg002:9E0E的这个比较中di只能被换成bx或si。而[bp+di+var_A]应该将其分为[bp+var_A]和di两部分来看待,而随着di值的变化,[bp+di+var_A]的最终指向的变量也会变化,这就是动态比较,而为了方便比较,通过兵种代码减去13h的操作,最终di的值的取值范围在0-7之间,例如di=0,那么[bp+di+var_A]实际上就是[bp+var_A],di=3,[bp+di+var_A]实际上是[bp+var_7],di=7,[bp+di+var_A]实际上是[bp+var_3],这样便实现了动态比较,比起通过逐个比较的代码而言相当简洁。
那么现在可以列明一个表格:
道具 長槍 步兵車 連弩 發石車 馬鎧 近衛鎧 無賴精神 俠義精神
道具代码 13 14 15 16 17 18 19 1A
di 0 1 2 3 4 5 6 7
变量 var_A var_9 var_8 var_7 var_6 var_5 var_4 var_3
兵种代码 00 1 3 4 6 7 9 0A
兵种 短兵 长兵 弓兵 連弩兵 輕騎兵 重騎兵 山賊 惡賊
这样每个道具就与升阶后的道具对应上了,但只对应上兵种可不行,还需要高出转职等级,不然可以体验一下10级的战车刘备。
seg002:9E16 loc_36D36: ; CODE XREF: sub_36C9C+95↑j
seg002:9E16 FF 76 EE push [bp+var_12]
seg002:9E19 9A DA 6A F6 1C call sub_23A3A ; 获取部队等级
seg002:9E1E 8B 7E FF mov di, word ptr [bp+var_1]
seg002:9E21 81 E7 FF 00 and di, 0FFh
seg002:9E25 89 7E E4 mov [bp+var_1C], di
seg002:9E28 38 43 E6 cmp [bp+di+var_1A], al ; 判断等级是否满足
seg002:9E2B 77 7A ja short loc_36DC7 ; 等級不夠使用道具
有了上面的表格和解释,如何比较等级的表格也不难得出:
道具 長槍 步兵車 連弩 發石車 馬鎧 近衛鎧 無賴精神 俠義精神
道具代码 13 14 15 16 17 18 19 1A
di 0 1 2 3 4 5 6 7
变量 var_1A var_19 var_18 var_17 var_16 var_15 var_14 var_13
等级 15 30 15 30 15 30 15 30
接下来会在屏幕上显示升阶后的兵种,并显示08變換級別的声音,再修改兵种后,还会显示部队兵种变化。
seg002:9E2D 8A 5B F6 mov bl, [bp+di+var_A]
seg002:9E30 2A FF sub bh, bh
seg002:9E32 03 DB add bx, bx
seg002:9E34 81 C3 A4 0A add bx, 0AA4h
seg002:9E38 89 5E E2 mov [bp+var_1E], bx
seg002:9E3B FF 37 push word ptr [bx]
seg002:9E3D 1E push ds
seg002:9E3E 68 F4 3A push 3AF4h ; "將部隊變換成%s."
seg002:9E41 6A 02 push 2
seg002:9E43 9A 42 32 F6 1C call sub_201A2 ;显示升阶后的兵种
seg002:9E48 83 C4 08 add sp, 8
seg002:9E4B 0B C0 or ax, ax
seg002:9E4D 75 03 jnz short loc_36D72
seg002:9E4F E9 74 06 jmp loc_373E6
seg002:9E52 loc_36D72: ; CODE XREF: sub_36C9C+D1↑j
seg002:9E52 6A 08 push 8 ; 08變換級別
seg002:9E54 68 80 54 push 5480h
seg002:9E57 9A 9C 57 F6 1C call sub_226FC ; 修改音效
seg002:9E5F 8A 43 F6 mov al, [bp+di+var_A]
seg002:9E62 FE C0 inc al ; 升职后兵种代码
seg002:9E64 50 push ax
seg002:9E65 FF 76 EE push [bp+var_12]
seg002:9E68 9A 68 93 F6 1C call sub_262C8 ; 修改部队兵种系代码
seg002:9E83 8B 5E E2 mov bx, [bp+var_1E]
seg002:9E86 FF 37 push word ptr [bx]
seg002:9E88 FF 76 EE push [bp+var_12]
seg002:9E8B 9A 58 39 F6 1C call sub_208B8 ; 名字的偏移地址
seg002:9E90 50 push ax
seg002:9E91 1E push ds
seg002:9E92 68 05 3B push offset aSS_0 ; "%s變成%s了!"
seg002:9E95 loc_36DB5: ; CODE XREF: sub_36C9C+6FE↓j
seg002:9E95 6A 00 push 0
seg002:9E97 9A 42 32 F6 1C call sub_201A2 ;显示部队兵种变化
但需要注意一个细节,就是seg002:9E34为什么是把bx的值加上0AA4h,在兵种的显示部分已经讲过了兵种的指引地址是从ds:0AA2开始的,而升阶后的兵种都是当前兵种的下一个兵种,将当前兵种对应的指引地址+2就是升阶后的兵种,个人认为这肯定不是一种巧合,而是故意设计的,因为这样可以方便代码的设计。
这样关于01變換用的道具就讲完了,接下来还要介绍02攻擊用和03恢復用的道具,这俩类道具最大的特点就是,大多数都有策略与之匹配,如焦熱書和焦熱,補給和麥,豪雨書和偽裝,也有些特例,玉璽、赦命書、援軍報告这三个道具虽然属于03恢復用道具,但没有对应的策略(本来就不是使用类道具),而大漩渦大山崩等大策略以及牽制挑撥欺壓三种策略没有对应的道具(不然谁买一大堆焦熱書)。
作者:
漫漫苦短 时间: 2026-2-19 19:19 标题: 十、道具的使用(02攻擊用类)(1)
从七到九节介绍了01變換用类的道具的使用的相关代码,而02攻擊用类的道具有一个较明显的特点,就是和部队所使用的策略部分是可以对应上的,并且很多特点是有共同特点的。
在介绍01變換用类,有一段将序号13到1A的道具判断的代码语句,如果不满足seg002:9DAB或seg002:9DB4两处的条件,就会跳到loc_36DD5。
seg002:9DAB 80 7E 08 13 cmp byte ptr [bp+arg_2], 13h
seg002:9DAF 73 03 jnb short loc_36CD4
seg002:9DB1 E9 01 01 jmp loc_36DD5
seg002:9DB4 80 7E 08 1A cmp byte ptr [bp+arg_2], 1Ah
seg002:9DB8 76 03 jbe short loc_36CDD
seg002:9DBA E9 F8 00 jmp loc_36DD5
02變換用类就是从loc_36DD5的一段连续代码,还是分段进行分析。
seg002:9EB5 loc_36DD5: ; CODE XREF: sub_36C9C+35↑j ; sub_36C9C+3E↑j
seg002:9EB5 80 7E 08 22 cmp byte ptr [bp+arg_2], 22h
seg002:9EB9 72 06 jb short loc_36DE1 ; 无符号小于跳转
seg002:9EBB 80 7E 08 2A cmp byte ptr [bp+arg_2], 2Ah
seg002:9EBF 76 03 jbe short loc_36DE4 ; 无符号小于等于跳转
seg002:9EC1 loc_36DE1: ; CODE XREF: sub_36C9C+13D↑j
seg002:9EC1 E9 FC 00 jmp loc_36EE0
seg002:9EC4 loc_36DE4: ; CODE XREF: sub_36C9C+143↑j
seg002:9EC4 8A 46 08 mov al, byte ptr [bp+arg_2]
seg002:9EC7 2A E4 sub ah, ah
seg002:9EC9 8B C8 mov cx, ax
seg002:9ECB 2D 22 00 sub ax, 22h
seg002:9ECE BA 03 00 mov dx, 3
seg002:9ED1 8B DA mov bx, dx
seg002:9ED3 99 cwd
seg002:9ED4 F7 FB idiv bx
seg002:9ED6 88 56 FD mov [bp+var_3], dl
seg002:9ED9 B8 22 00 mov ax, 22h
seg002:9EDC 2B C1 sub ax, cx
seg002:9EDE 99 cwd
seg002:9EDF F7 FB idiv bx
seg002:9EE1 04 02 add al, 2
seg002:9EE3 88 46 FF mov [bp+var_1], al
seg002:9EB5和seg002:9EBB这两条判断和前面的部分都很类似,就是换了个判断的范围,如果同时满足道具代码arg_2大于等于22和小于等于2A两个条件,就会跳转到loc_36DE4处。
而从seg002:9EC4到seg002:9EE3的部分又是一系列复杂的代码,使用表格的方法,就可以很清晰的分析出这段代码的作用。
道具 落石書 山崩書 山洪書 漩渦書 濁流書 海嘯書 焦熱書 火龍書 猛火書
道具代码 22 23 24 25 26 27 28 29 2A
道具代码-22h 0 1 2 3 4 5 6 7 8
除以3取余数 0 1 2 0 1 2 0 1 2
var_3的结果 0 1 2 0 1 2 0 1 2
除以-3整数商 0 0 0 -1 -1 -1 -2 -2 -2
var_1的结果 2 2 2 1 1 1 0 0 0
那么这段代码的用处就能很直观看出来,将这九个道具分类,var_1变量中,属于火类道具设为0,属于水类道具设为1,属于山类道具设为2,var_3变量则是另一个作用,属于第一档威力的道具则0,第二档威力的道具则是1,第三档威力的道具则是2,这样九个道具因道具代码不同,var_1和var_3的值不会完全一致。接下来的这段代码就是容易看懂了。
seg002:9EE6 0A C0 or al, al ; 判断是不是火类道具
seg002:9EE8 75 13 jnz short loc_36E1D
seg002:9EEA 68 6A CF push 0CF6Ah
seg002:9EED 9A D8 3F F2 2C call sub_30EF8 ; 获取天气信息 0晴 1雲 2雨
seg002:9EF2 3C 02 cmp al, 2 ; 判断是不是雨
seg002:9EF4 75 07 jnz short loc_36E1D
seg002:9EF6 1E push ds
seg002:9EF7 68 30 3B push 3B30h ; "雨天不能使用火攻!"
seg002:9EFA E9 96 05 jmp loc_373B3
seg002:9EFD loc_36E1D: ; CODE XREF: sub_36C9C+16C↑j ; sub_36C9C+178↑j
seg002:9EFD 8A 5E FF mov bl, [bp+var_1]
seg002:9F00 2A FF sub bh, bh
seg002:9F02 03 DB add bx, bx
seg002:9F04 FF B7 AA 39 push word ptr [bx+39AAh]
seg002:9F08 1E push ds
seg002:9F09 68 43 3B push 3B43h ; "用%s攻擊敵人."
seg002:9F0C 6A 00 push 0
seg002:9F0E 9A 42 32 F6 1C call sub_201A2
我们都知道雨天是不能使用火类道具,这段代码的前半部分seg002:9EE6-9EFA就是实现这个功能的,后半部分seg002:9EFD-9F0E是输出一段语句,就是当使用这类道具选择敌军前会弹出一个文字框,就是seg002:9F09的这行文字"用%s攻擊敵人.",每种道具使用前会根据var_1的不同道具%s替换为不同的文字,从这两个例子就能看出这前面先将道具代码分类的作用。
下一节介绍使用这九个道具是如何选择敌军,以及如何判断使用敌军对象是否能被攻击。
[ 本帖最后由 漫漫苦短 于 2026-2-19 19:59 编辑 ]
作者:
danelchen 时间: 2026-2-21 12:05
前人之事,后世之师。
先点赞留痕。
作者:
漫漫苦短 时间: 2026-3-31 19:19 标题: 十一、道具的使用(02攻擊用类)(2)
本节重点就是介绍其中的目标部队是否可以被道具攻击,先看完整的代码,其中var_2为部队代码。
seg002:9F2A 88 46 FE mov [bp+var_2], al
seg002:9F2D 3C FF cmp al, 0FFh
seg002:9F2F 75 03 jnz short loc_36E54
seg002:9F31 E9 69 05 jmp loc_373BD
seg002:9F34 loc_36E54:
seg002:9F34 B0 0E mov al, 0Eh
seg002:9F36 F6 66 FE mul [bp+var_2]
seg002:9F39 05 76 D0 add ax, 0D076h
seg002:9F3C 89 46 E0 mov [bp+var_20], ax
seg002:9F3F 50 push ax
seg002:9F40 9A F6 40 F2 2C call sub_31016 ; 判断属于我军1还是敌军0
seg002:9F45 56 push si
seg002:9F46 8B F8 mov di, ax
seg002:9F48 9A F6 40 F2 2C call sub_31016 ; 判断属于我军1还是敌军0
seg002:9F4D 3B F8 cmp di, ax
seg002:9F4F 74 2B jz short loc_36E9C
seg002:9F51 8B 46 E0 mov ax, [bp+var_20]
seg002:9F54 89 46 FA mov word ptr [bp+var_6], ax
seg002:9F57 50 push ax
seg002:9F58 9A 9C 40 F2 2C call sub_30FBC ; 获取部队横纵坐标偏移地址
seg002:9F5D 8B D8 mov bx, ax
seg002:9F5F 8B 07 mov ax, [bx]
seg002:9F61 89 46 F8 mov word ptr [bp+var_8], ax
seg002:9F64 8D 5E F8 lea bx, [bp+var_8]
seg002:9F67 9A B2 AF F2 2C call sub_37ED2 ; 获取部队地形代码
seg002:9F6C 8A D0 mov dl, al
seg002:9F6E 8A 46 FF mov al, [bp+var_1]
seg002:9F71 32 DB xor bl, bl
seg002:9F73 9A 84 95 F2 2C call sub_364A4 ; 判断目标部队是否可以被道具攻击
seg002:9F78 0B C0 or ax, ax
同样还是对比第陆章策略使用中的类似代码。
seg002:A5D4 88 46 FE mov [bp+var_2], al
seg002:A5D7 3C FF cmp al, 0FFh
seg002:A5D9 74 30 jz short loc_3752B
seg002:A5DB B0 0E mov al, 0Eh
seg002:A5DD F6 66 FE mul [bp+var_2]
seg002:A5E0 05 76 D0 add ax, 0D076h
seg002:A5E3 89 46 EC mov [bp+var_14], ax
seg002:A5E6 50 push ax
seg002:A5E7 9A 9C 40 F2 2C call sub_30FBC ; 获取部队横纵坐标偏移地址
seg002:A5EC 8B D8 mov bx, ax
seg002:A5EE 8B 07 mov ax, [bx]
seg002:A5F0 89 46 F2 mov [bp+var_E], ax
seg002:A5F3 8D 5E F2 lea bx, [bp+var_E]
seg002:A5F6 9A B2 AF F2 2C call sub_37ED2 ; 获取部队地形代码
seg002:A5FB 8A D0 mov dl, al
seg002:A5FD 8A 46 FD mov al, [bp+var_3]
seg002:A600 32 DB xor bl, bl
seg002:A602 9A 84 95 F2 2C call sub_364A4 ; 判断目标部队是否可以被策略攻击
seg002:A607 0B C0 or ax, ax
可以看到下面部分的代码时判断目标部队是否可以被道具攻击中明显多余的部分,那么是否有其他的作用?
seg002:9F3F 50 push ax
seg002:9F40 9A F6 40 F2 2C call sub_31016 ; 判断属于我军1还是敌军0
seg002:9F45 56 push si
seg002:9F46 8B F8 mov di, ax
seg002:9F48 9A F6 40 F2 2C call sub_31016 ; 判断属于我军1还是敌军0
seg002:9F4D 3B F8 cmp di, ax
seg002:9F4F 74 2B jz short loc_36E9C
然而偏偏这是一段略显多余的代码,因为在此之前就已经判断过目标部队指向是否合法(即两支部队是否不不同阵营)。
[ 本帖最后由 漫漫苦短 于 2026-3-31 20:35 编辑 ]
作者:
漫漫苦短 时间: 2026-5-9 19:19 标题: 十二、道具的使用(02攻擊用类)(3)
接下来几节的主要内容是关于伤害的计算,关于第十节通过分析得出的var_1和var_3的值在这一节就派上用场了。
和策略的使用类似,首先都要通过一个函数sub_3AA64,其实这个函数,总的来说这是个结算系统,这个结算系统后续的一章会专门来介绍,关于这个结算系统的整体框架分析,目前没有看到相关资料。
道具:
seg002:9F9C 8A 44 02 mov al, [si+2]
seg002:9F9F 50 push ax
seg002:9FA0 FF 76 FA push word ptr [bp+var_6]
seg002:9FA3 9A 9A AE F2 2C call sub_37DBA
seg002:9FA8 50 push ax
seg002:9FA9 6A 03 push 3
seg002:9FAB 8A 46 FF mov al, [bp+var_1] ; 火水石类道具的类型
seg002:9FAE C0 E0 04 shl al, 4
seg002:9FB1 0A 46 FD or al, [bp+var_3] ; 火水石类道具的指向范围
seg002:9FB4 50 push ax
seg002:9FB5 68 02 D3 push 0D302h
seg002:9FB8 9A 44 DB F2 2C call sub_3AA64
seg002:9FBD E9 DF FE jmp loc_36DBF
策略:
seg002:A630 8A 44 02 mov al, [si+2]
seg002:A633 50 push ax
seg002:A634 8A 46 FE mov al, [bp+var_2]
seg002:A637 50 push ax
seg002:A638 6A 02 push 2
seg002:A63A 8A 46 FD mov al, [bp+var_3] ; 火水石策略的类型
seg002:A63D C0 E0 04 shl al, 4
seg002:A640 0A 46 F9 or al, [bp+var_7] ; 火水石策略的指向范围
seg002:A643 88 46 EA mov [bp+var_16], al
seg002:A646 50 push ax
seg002:A647 68 02 D3 push 0D302h
seg002:A64A 9A 44 DB F2 2C call sub_3AA64
要调用sub_3AA64函数,需要传入5个值。seg002:9F9F与seg002:A633传入的值,对应sub_3AA64的arg_8,也就是行动方的战场代码;seg002:9FA8与seg002:A637传入的值,对应sub_3AA64的arg_6,也就是目标方的战场代码,seg002:9FA9与seg002:A638不同,策略是2,道具是3;seg002:9FB4与seg002:A646传入的值,对应sub_3AA644的arg_2,就是根据var_3和var_7的值的计算结果。
设道具的类型即var_1为x,道具的指向范围即var_3为y,最终传入arg_2的值为z,可以得出公式 z = 16x + y。
也就是说火龙书的计算结果为1,漩涡书的计算结果为0x10(16),山洪书的计算结果为0x22(34)。这个结果与策略基本威力目前还看不到什么相关联的地方。
焦热书 200 火龙书 600 猛火书 1000
漩涡书 300 浊流书 700 海啸书 1100
落石书 400 山崩书 800 山洪书 1200
然后是sub_3AA64的开头部分,可以看到arg_2值保存在si+0B中,可以看到arg_4值保存在si+0A中。
seg002:DB44 sub_3AA64 proc far
seg002:DB44
seg002:DB44 arg_0 = word ptr 6
seg002:DB44 arg_2 = byte ptr 8
seg002:DB44 arg_4 = byte ptr 0Ah
seg002:DB44 arg_6= byte ptr 0Ch
seg002:DB44 arg_8= byte ptr 0Eh
seg002:DB44
seg002:DB44 C8 1A 00 00 enter 1Ah, 0
seg002:DB48 57 push di
seg002:DB49 56 push si
seg002:DB4A 8B 76 06 mov si, [bp+arg_0]
seg002:DB4D 8A 46 0A mov al, [bp+arg_4]
seg002:DB50 88 44 0A mov [si+0Ah], al
seg002:DB53 8A 46 08 mov al, [bp+arg_2]
seg002:DB56 88 44 0B mov [si+0Bh], al
接下来的代码是道具伤害和策略伤害的行动方策略命中和目标方策略闪避的计算,虽然道具是百分之百命中,但仍然计算了一遍。这里的sub_23A3A函数和sub_294F0函数都已经在第肆章介绍过了,这里有个比较奇怪的地方就是智力算了两次,并且结果都是一样的,不知道为啥在转换成汇编代码时出现了重复。最终行动方策略命中的结果保存在var_6,目标方策略闪避的结果保存在var_2。
行动方:
seg002:D2B9 8A 5C 08 mov bl, [si+8]
seg002:D2BC 2A FF sub bh, bh
seg002:D2BE 03 DB add bx, bx
seg002:D2C0 FF 70 04 push word ptr [bx+si+4]
seg002:D2C3 8B FB mov di, bx
seg002:D2C5 9A DA 6A F6 1C call sub_23A3A ; 获取行动方的等级
seg002:D2CA 03 FE add di, si
seg002:D2CC FF 75 04 push word ptr [di+4]
seg002:D2CF 89 46 EC mov [bp+var_14], ax
seg002:D2D2 9A 90 C5 F6 1C call sub_294F0 ; 获取行动方的智力
seg002:D2D7 FF 75 04 push word ptr [di+4]
seg002:D2DA 89 46 EA mov [bp+var_16], ax
seg002:D2DD 9A 90 C5 F6 1C call sub_294F0 ; 获取行动方的智力
seg002:D2E2 2A E4 sub ah, ah
seg002:D2E4 8B C8 mov cx, ax
seg002:D2E6 8A 46 EC mov al, byte ptr [bp+var_14]
seg002:D2E9 F6 66 EA mul byte ptr [bp+var_16]
seg002:D2EC BA 64 00 mov dx, 100
seg002:D2EF 8B DA mov bx, dx
seg002:D2F1 2B D2 sub dx, dx
seg002:D2F3 F7 F3 div bx
seg002:D2F5 03 C8 add cx, ax ; 等级*智力÷100+智力
seg002:D2F7 89 4E FA mov word ptr [bp+var_6], cx
目标方:
seg002:D2FA 8A 5C 09 mov bl, [si+9]
seg002:D2FD 03 DB add bx, bx
seg002:D2FF FF 70 04 push word ptr [bx+si+4]
seg002:D302 8B FB mov di, bx
seg002:D304 9A DA 6A F6 1C call sub_23A3A ; 获取目标方的等级
seg002:D309 03 FE add di, si
seg002:D30B FF 75 04 push word ptr [di+4]
seg002:D30E 89 46 E8 mov [bp+var_18], ax
seg002:D311 9A 90 C5 F6 1C call sub_294F0 ; 获取目标方的智力
seg002:D316 FF 75 04 push word ptr [di+4]
seg002:D319 89 46 E6 mov [bp+var_1A], ax
seg002:D31C 9A 90 C5 F6 1C call sub_294F0 ; 获取目标方的智力
seg002:D321 2A E4 sub ah, ah
seg002:D323 8B C8 mov cx, ax
seg002:D325 8A 46 E8 mov al, byte ptr [bp+var_18]
seg002:D328 F6 66 E6 mul byte ptr [bp+var_1A]
seg002:D32B BA 64 00 mov dx, 100
seg002:D32E 8B DA mov bx, dx
seg002:D330 2B D2 sub dx, dx
seg002:D332 F7 F3 div bx
seg002:D334 03 C8 add cx, ax ; 等级*智力÷100+智力
seg002:D336 C1 E9 02 shr cx, 2 ; (等级*智力÷100+智力)/4
seg002:D339 89 4E FE mov word ptr [bp+var_2], cx
[ 本帖最后由 漫漫苦短 于 2026-5-9 21:09 编辑 ]
作者:
漫漫苦短 时间: 2026-5-19 19:19 标题: 十三、道具的使用(02攻擊用类)(4)
请记住下面这行代码,很简单,这个的作用就是将DI寄存器的值设为0。
seg002:D33C 33 FF xor di, di
接下来的内容,我在请教,怎么修改三国志英杰传的基本策略伤害?这个帖子中的看到孝直和godtype两位前辈的回答,我把他俩回答贴一下。
原帖由
阿尔法孝直 于 2015-2-8 18:33 发表
从main.exe的0x30C5E开始:
30C5E:mov al, [si+0Bh] ;[si+0Bh]的高4位是策略类型,值为0=火,1=水,2=山;低4位是策略威力基数,值为0=低级策略(例如焦热、大漩涡),1=中级策略,2=高级策略
30C61:shr al, 4
30C64:sub ah, ah ;获得AX=策略类型
30C66:mov cl, [si+0Bh]
30C69:and cx, 0Fh ;获得CX=策略威力基数
30C6C:shl cx, 2
30C6F:add cx, ax
30C71:inc cx
30C72:inc cx
30C73:imul ax, cx, 100 ;结果是策略基本威力=(策略威力基数*4+2+策略类型)*100
30C76:mov [bp+var_8], ax
如果是焦热(或大焦热),策略类型=0,策略威力基数=0,策略基本威力=(0*4+2+0)*100=200
如果是海啸,策略类型=1,策略威力基数=2,策略基本威力=(2*4+2+1)*100=1100 ...
原帖由 godtype 于 2015-2-8 20:08 发表
0x30C5E 8A 44 0B mov al, [si+0Bh] ;[si+0Bh]的高4位是策略类型,值为0=火,1=水,2=山;低4位是策略威力基数,值为0=低级策略(例如焦热、大漩涡),1=中级策略,2=高级策略
0x30C61 C0 E8 04 shr al, 4
0x30C64 2A E4 sub ah, ah ;获得AX=策略类型
0x30C66 8A 4C 0B mov cl, [si+0Bh]
0x30C69 83 E1 0F and cx, 0Fh ;获得CX=策略威力基数
0x30C6C C1 E1 02 shl cx, 2
0x30C6F 03 C8 add cx, ax
0x30C71 41 inc cx
0x30C72 41 inc cx
0x30C73 6B C1 64 imul ax, cx, 64 ;结果是策略基本威力=(策略威力基数*4+2+策略类型)*100
其实就是代码中seg002: D33E-D353的内容,最后将这个结果保存到var_8中,当时就是看到俩位的回答,就在此基础上反向查找,找到了计算si+0B的过程也就是上2节的介绍的。
seg002:D33E 8A 44 0B mov al, [si+0Bh] ; [si+0Bh]的高4位是策略的类型, 0火, 1水, 2山
seg002:D33E ; 低4位是策略指向范围
seg002:D341 C0 E8 04 shr al, 4
seg002:D344 2A E4 sub ah, ah
seg002:D346 8A 4C 0B mov cl, [si+0Bh]
seg002:D349 83 E1 0F and cx, 0Fh
seg002:D34C C1 E1 02 shl cx, 2
seg002:D34F 03 C8 add cx, ax
seg002:D351 41 inc cx
seg002:D352 41 inc cx
seg002:D353 6B C1 64 imul ax, cx, 100 ; 策略基本威力=(策略指向范围*4+2+策略类型)*100
seg002:D356 89 46 F8 mov [bp+var_8], ax
接下来是一个判断,就是根据si+0A判断使用的道具还是策略,使用的道具就跳到seg002: D39F,seg002: D35F-D39D的内容就是计算行动方的策略能力,因为道具的公式不包括行动方的策略能力所以跳过了。
seg002:D359 80 7C 0A 02 cmp byte ptr [si+0Ah], 2 ; 判断是使用策略还是道具
seg002:D35D 75 40 jnz short loc_3A2BF
然后就是计算-目标方的策略能力,并且将最终保存在var_C,var_A中,这个结果也称为伤害(无随机结果),可以看到seg002: D3C9是di-目标方的策略能力,由于前面di设为0,所以这步的结果肯定是个负数。
seg002:D39F loc_3A2BF: ; CODE XREF: sub_3A060+21D↑j
seg002:D39F 8A 5C 09 mov bl, [si+9]
seg002:D3A2 2A FF sub bh, bh
seg002:D3A4 03 DB add bx, bx
seg002:D3A6 FF 70 04 push word ptr [bx+si+4]
seg002:D3A9 89 5E E0 mov [bp+var_20], bx
seg002:D3AC 9A DA 6A F6 1C call sub_23A3A ; 获取目标方的等级
seg002:D3B1 8B 5E E0 mov bx, [bp+var_20]
seg002:D3B4 FF 70 04 push word ptr [bx+si+4]
seg002:D3B7 89 46 DE mov [bp+var_22], ax
seg002:D3BA 9A 90 C5 F6 1C call sub_294F0
seg002:D3BF F6 66 DE mul byte ptr [bp+var_22]
seg002:D3C2 B9 32 00 mov cx, 50
seg002:D3C5 2B D2 sub dx, dx
seg002:D3C7 F7 F1 div cx ; 等级*智力÷50
seg002:D3C9 2B F8 sub di, ax ; di-等级*智力÷50
seg002:D3CB 8B 5E E0 mov bx, [bp+var_20]
seg002:D3CE FF 70 04 push word ptr [bx+si+4]
seg002:D3D1 9A 90 C5 F6 1C call sub_294F0
seg002:D3D6 2A E4 sub ah, ah
seg002:D3D8 2B F8 sub di, ax ; di-智力
seg002:D3DA 03 7E F8 add di, [bp+var_8] ; di+策略基本威力
seg002:D3DD 89 7E F4 mov [bp+var_C], di
seg002:D3E0 C7 46 F6 00 00 mov [bp+var_A], 0
接下来就是伤害计算中第一次随机数消耗可以看到,虽然道具是百分之百命中的,但像前面计算行动方策略命中一样,仍然产生了随机数消耗,有可能一开始游戏的设计中并没有规定道具是百分之百命中的。
seg002:D3E5 8B 46 FA mov ax, word ptr [bp+var_6] ; 行动方策略命中
seg002:D3E8 9A E0 3D F6 1C call sub_20D40 ; 策略命中随机数
seg002:D3ED 88 46 FD mov [bp+var_3], al
seg002: D3F0-D448的内容是判断目标方是否是文官(军乐队/运输队/妖术师,策略闪避*2,伤害÷2),判断是否要增加四分之一的伤害(火道具目标方在森林,水道具雨天),后续会介绍。
然后就是第二次随机数消耗,可以看到是先消耗策略命中随机数,再消耗道具伤害随机数。如果道具伤害小于50,并不会消耗道具伤害随机数,如果道具伤害大于等于50且小于100,虽然结果必然是0,但是也消耗了随机数。随后将伤害随机数与道具伤害(无随机结果)相加,最终结果的结果就是有伤害的。
seg002:D44B 8B 46 F4 mov ax, [bp+var_C]
seg002:D44E B9 32 00 mov cx, 50
seg002:D451 2B D2 sub dx, dx
seg002:D453 F7 F1 div cx
seg002:D455 9A E0 3D F6 1C call sub_20D40 ; 道具伤害随机数
seg002:D45A 2B D2 sub dx, dx
seg002:D45C 01 46 F4 add [bp+var_C], ax
seg002:D45F 11 56 F6 adc [bp+var_A], dx
最后是一个伤害与目标方兵力的比较,防止伤害超出目标方兵力,如果伤害超出目标方兵力,将最终伤害修改为目标方兵力,将最终伤害保存在si+1F中。
seg002:D477 FF 76 F6 push [bp+var_A]
seg002:D47A FF 76 F4 push [bp+var_C]
seg002:D47D 8A 5C 09 mov bl, [si+9]
seg002:D480 2A FF sub bh, bh
seg002:D482 03 DB add bx, bx
seg002:D484 2B D2 sub dx, dx
seg002:D486 8B 40 0F mov ax, [bx+si+0Fh] ; 目标方兵力
seg002:D489 9A 44 3C F6 1C call sub_20BA4
seg002:D48E 89 44 1F mov [si+1Fh], ax
这便是道具伤害的完整计算过程,这其中的计算又有怎样的问题,为什么会导致焦热书秒杀的BUG,下一节再重新回顾整个过程,找出BUG的根源在哪里。
[ 本帖最后由 漫漫苦短 于 2026-5-19 20:52 编辑 ]
| 欢迎光临 轩辕春秋文化论坛 (http://xycq.org.cn/forum/) |
Powered by Discuz! 5.0.0 |