标题: DOS版三国志英杰传的研究心得——贰, 初入英杰传代码
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-10 19:19 资料 短消息 只看该作者
DOS版三国志英杰传的研究心得——贰

这一章正式进入汇编语言中的函数研究,关于该从那个切入口开始探讨英杰传代码算本人是想了一个多月,前面的序和一就当是在铺垫了。得出的结论是其实我没必要把这些写得太细,当然也不是让人看得云里雾里,我觉得如果能对反汇编代码感兴趣,自然也能通过其他渠道了解相关知识,而兴趣不足,只当是想看个结论的,当然也会觉得那些很高深莫测。

接下来进入正文,这个英杰传代码的切入口,我想不如从一个相对独立且没有什么理解难度的地方入手。


顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-10 19:19 资料 短消息 只看该作者
一、回數与天气

回数其实就是回合的意思,我为了忠实于英杰传的源代码而这么命名,那么这两者这间有什么关联,并且与其他内容如何联系,这节先简要研究一下。

这两者最大的关系是它们在游戏中的内存空间是在一起的,而且都和DS:CF6A这个地址有关,下面看以下英杰传代码,反汇编代码都是可以写注释,每条语句后面加上; 就是它的注释,也可以放在两条语句中间。

                             ; si在此之前已经=CF6A
seg002:2D16 C6 44 02 00                    mov     byte ptr [si+2], 0   ;現在回數设为0
seg002:2D1A C6 44 04 01                    mov     byte ptr [si+4], 1   ;第0回數天气设为1



                             ; di在此之前已经=CF6A
seg002:3D26 8A 45 03                       mov     al, [di+3]        ;限制回數
seg002:3D29 38 45 02                       cmp     [di+2], al        ;比较現在回數与限制回數
seg002:3D2C 72 03                          jb      short loc_30C51   ; [di+2]<[di+3] 跳转
seg002:3D2E E9 C9 00                       jmp     loc_30D1A
seg002:3D31                loc_30C51:                              ; CODE XREF: sub_30C28+24↑j
seg002:3D31 57                             push    di
seg002:3D32 9A 62 3E F2 2C                 call    sub_30D82         ;更新天气值

seg002:3DFA                loc_30D1A:                              ; CODE XREF: sub_30C28+26↑j
seg002:3DFA 1E                             push    ds
seg002:3DFB 68 28 30                       push    offset asc_437F8 ; "戰鬥拖長了,所以要撤退.\n"

通过这两段我们可以看到,ds:[CF6A+2]保存的是現在回數,ds:[CF6A+3]保存的是限制回數,ds:[CF6A+4]保存的是天气值,每当回數+1都会更新一次天气值这与我们在游戏中看到的是一样的。

seg002:3D72 57                             push    di
seg002:3D73 9A D8 3F F2 2C                 call    sub_30EF8      ;天气
seg002:3D78 8A D8                          mov     bl, al
seg002:3D7A 2A FF                          sub     bh, bh
seg002:3D7C 03 DB                          add     bx, bx
seg002:3D7E FF B7 14 2D                    push    word ptr [bx+2D14h]   ; 0"晴", 1"雲", 2"雨"
seg002:3D82 8A 45 02                       mov     al, [di+2]            ;現在回數
seg002:3D85 2A E4                          sub     ah, ah
seg002:3D87 40                             inc     ax
seg002:3D88 50                             push    ax
seg002:3D89 1E                             push    ds
seg002:3D8A 68 42 30                       push    offset asc_43812 ; "第%w回合 %s"
seg002:3D8D 6A 00                          push    0
seg002:3D8F 9A 42 32 F6 1C                 call    sub_201A2
seg002:3D94 83 C4 0A                       add     sp, 0Ah

dseg:2D0A B4 B8 00       asc_434DA       text "Big5", '晴',0      ; DATA XREF: dseg:2D14↓o
dseg:2D0D B6 B3 00       asc_434DD       text "Big5", '雲',0      ; DATA XREF: dseg:2D16↓o
dseg:2D10 AB 42 00       asc_434E0       text "Big5", '雨',0      ; DATA XREF: dseg:2D18↓o

dseg:2D14 0A 2D                          dw offset asc_434DA     ; "晴"
dseg:2D16 0D 2D                          dw offset asc_434DD     ; "雲"
dseg:2D18 10 2D                          dw offset asc_434E0     ; "雨"

这就是我们在每个回合开始的时候看到的本回合信息,%w会被替换为(現在回數+1),而%s会被替换为"晴", "雲", "雨",根据bx来确定。
[bx+2D14h]可能看着既熟悉又陌生,这其实是汇编语句中的动态获取内存地址的值的方法。
当sub_30EF8函数返回的是0时,获取的是DS:[0+2D14], 也就是2D0A,指向"晴"这个字符串,返回的是2时,获取的是DS:[2+2+2D14]=DS:[2D18]

                      ;这个函数arg_0值只有CF6A
seg002:35E6 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:35E9 2A E4                          sub     ah, ah
seg002:35EB 8A 47 03                       mov     al, [bx+3]      ;限制回數
seg002:35EE 50                             push    ax
seg002:35EF 8A 47 02                       mov     al, [bx+2]      ;現在回數
seg002:35F2 40                             inc     ax
seg002:35F3 50                             push    ax
seg002:35F4 1E                             push    ds
seg002:35F5 68 6D 2F                       push    offset asc_4373D ; "現在回數%2w/%2w"
seg002:35F8 9A 32 32 F6 1C                 call    sub_20192
seg002:35FD 83 C4 08                       add     sp, 8
seg002:3600 EB 15                          jmp     short loc_30537
seg002:3602                loc_30522:                              ; CODE XREF: sub_303DA+12A↑j
seg002:3602 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:3605 2A E4                          sub     ah, ah
seg002:3607 8A 47 03                       mov     al, [bx+3]       ;限制回數
seg002:360A 50                             push    ax
seg002:360B 1E                             push    ds
seg002:360C 68 7E 2F                       push    offset asc_4374E ; "限制回數%2w"
seg002:360F 9A 32 32 F6 1C                 call    sub_20192
seg002:3614 83 C4 06                       add     sp, 6
seg002:3617                loc_30537:                              ; CODE XREF: sub_303DA+146↑j

通过上面的分析,这段是什么意思应该不难看出吧。

接下来就是如何更新天气的部分了,龙吟前辈已经给出了其中很详细的更新过程了,这部分就当是朝花夕拾了,想当初我刚开始研究找到这部分的代码,为了把main.exe的内容与之对应上,就费了一会功夫,希望今后其他人的研究就别这么吃力了。

seg002:3E62                sub_30D82       proc far                ; CODE XREF: sub_30C28+2A↑P
seg002:3E62
seg002:3E62                var_2           = byte ptr -2
seg002:3E62                var_1           = byte ptr -1
seg002:3E62                arg_0           = word ptr  6           ;这个函数arg_0值只有CF6A
seg002:3E62
seg002:3E62 C8 02 00 00                    enter   2, 0
seg002:3E66 56                             push    si              ;在栈中保存si原来的值
seg002:3E67 8B 76 06                       mov     si, [bp+arg_0]
seg002:3E6A B8 06 00                       mov     ax, 6
seg002:3E6D 9A E0 3D F6 1C                 call    sub_20D40        ;产生天气随机数
seg002:3E72 88 46 FE                       mov     [bp+var_2], al
seg002:3E75 8A 44 04                       mov     al, [si+4]       ;获取上回合天气代码
seg002:3E78 88 46 FF                       mov     [bp+var_1], al
seg002:3E7B 3A 46 FE                       cmp     al, [bp+var_2]   ;比较上回合天气代码与天气随机数
seg002:3E7E 76 05                          jbe     short loc_30DA5        ;
seg002:3E80 FE 4E FF                       dec     [bp+var_1]       ;上回合天气代码大于随机数,更新为代码-1
seg002:3E83 EB 0B                          jmp     short loc_30DB0
seg002:3E85                loc_30DA5:                              ; CODE XREF: sub_30D82+1C↑j
seg002:3E85 8A 46 FE                       mov     al, [bp+var_2]
seg002:3E88 38 46 FF                       cmp     [bp+var_1], al     ;比较上回合天气代码与天气随机数
seg002:3E8B 73 03                          jnb     short loc_30DB0
seg002:3E8D FE 46 FF                       inc     [bp+var_1]      ;上回合天气代码小于随机数,更新为代码+1
seg002:3E90                loc_30DB0:                              ; CODE XREF: sub_30D82+21↑j sub_30D82+29↑j
seg002:3E90 80 7E FF 00                    cmp     [bp+var_1], 0     ;比较更新后的天气代码与0
seg002:3E94 75 06                          jnz     short loc_30DBC
seg002:3E96 C6 44 04 05                    mov     byte ptr [si+4], 5     ;相等,天气代码由0改为5
seg002:3E9A EB 12                          jmp     short loc_30DCE
seg002:3E9C                loc_30DBC:                              ; CODE XREF: sub_30D82+32↑j
seg002:3E9C 80 7E FF 05                    cmp     [bp+var_1], 5     ;比较更新后的天气代码与5
seg002:3EA0 75 06                          jnz     short loc_30DC8
seg002:3EA2 C6 44 04 00                    mov     byte ptr [si+4], 0     ;相等,天气代码由5改为0
seg002:3EA6 EB 06                          jmp     short loc_30DCE
seg002:3EA8                loc_30DC8:                              ; CODE XREF: sub_30D82+3E↑j
seg002:3EA8 8A 46 FF                       mov     al, [bp+var_1]
seg002:3EAB 88 44 04                       mov     [si+4], al      ;保存本回合天气代码
seg002:3EAE                loc_30DCE:                              ; CODE XREF: sub_30D82+38↑j
seg002:3EAE                                                        ; sub_30D82+44↑j
seg002:3EAE 5E                             pop     si      ;还原si原来的值
seg002:3EAF C9                             leave
seg002:3EB0 CA 02 00                       retf    2
seg002:3EB0                sub_30D82       endp

似乎还漏了一点,为什么0, 1, 2代表0"晴",3代表1"雲",4, 5代表2"雨",看了这部分就不会迷惑了吧。

seg002:3FD8                sub_30EF8       proc far
seg002:3FD8
seg002:3FD8                arg_0           = word ptr  6        ;这个函数传入的参数值只有CF6A
seg002:3FD8
seg002:3FD8 55                             push    bp
seg002:3FD9 8B EC                          mov     bp, sp
seg002:3FDB 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:3FDE 8A 5F 04                       mov     bl, [bx+4]     ;获取天气代码
seg002:3FE1 2A FF                          sub     bh, bh
seg002:3FE3 8A 87 4E 2E                    mov     al, [bx+2E4Eh]
seg002:3FE7 C9                             leave
seg002:3FE8 CA 02 00                       retf    2
seg002:3FE8                sub_30EF8       endp

dseg:2E4E 00 00 00 01 02 02                 db 0, 0, 0, 1, 2, 2

[ 本帖最后由 漫漫苦短 于 2025-2-10 19:44 编辑 ]


顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-15 19:19 资料 短消息 只看该作者
二、地图代码和宽高、关卡状态

上节介绍了天气和回合都是与CF6A有关,但是只占用了DS段的[CF6A+2][CF6A+3][CF6A+4],也就是还有[CF6A][CF6A+1],那么它们又代表什么?
ds:CF6A占1字节,它代表的是地图代码,每一张地图都有唯一代码,注意徐州I和徐州II用的是同一幅地图,夏丘彭城新野襄阳雒天荡山的I和II都是如此,但葭萌关瓦口关宛许昌邺这些关虽然名字相同,但用的不是同一幅地图,地图代码是不一样的,用过龙吟的剧情编辑和地图编辑工具应该对此有所了解,有时间总结一下所有的地图代码。

seg002:3EB4                sub_30DD4       proc far                ; CODE XREF: sub_2FB7A+F↑P
seg002:3EB4
seg002:3EB4                var_C           = byte ptr -0Ch
seg002:3EB4                var_4           = byte ptr -4
seg002:3EB4                arg_0           = word ptr  6                   ;arg_0=CF6A
seg002:3EB4
seg002:3EB4 C8 0C 00 00                    enter   0Ch, 0
seg002:3EB8 8D 46 F4                       lea     ax, [bp+var_C]
seg002:3EBB 50                             push    ax
seg002:3EBC 68 5E C2                       push    0C25Eh
seg002:3EBF 9A 1E C5 F6 1C                 call    sub_2947E
seg002:3EC4 8B 5E 06                       mov     bx, [bp+arg_0]      ;BX=CF6A
seg002:3EC7 8A 46 FC                       mov     al, [bp+var_4]
seg002:3ECA 88 07                          mov     [bx], al            ;保存地图代码
seg002:3ECC C9                             leave
seg002:3ECD CA 02 00                       retf    2
seg002:3ECD                sub_30DD4       endp

这里先不分析sub_2947E的具体作用,但我们要注意的var_4并没有在seg002:3EB8-seg002:3EC4之前有类似mov [bp-var_4], al的代码,但却把var_4赋值给了al,但这其实不是程序有误使用未初始化的数据传值,因为有些函数需要的返回值不只1个可以直接保存在AX中,这时就有两个解决方法:

  1. 在传入函数参数的时候,先传入一个地址值,比如说lea ax, [bp+var_C]这句,就是获取var_C的地址的值保存在AX中,以指针的形式传给sub_2947E函数,这样sub_2947E函数可以以它的arg_0(bp+var_C的地址)为首地址修改其偏移的地址的值。
  2. 把返回值传入以一个地址为首开始的一段连续内存空间,并且返回该地址给AX。

2的方法在代码中的应用碰到了再介绍,2的方法识别起来更加容易,因为在函数的末尾通常会出现把将一个固定地址传给AX,而且在前文会对这个固定地址以及其偏移地址进行修改。

读取了地图代码后就可以进行一系列的操作了,比如读取这个地图的宽和高,保存在[CF68]和[CF69],CF68,CF69都比CF6A小,但没有用[CF6A-2]和[CF6A-1]的方式而是使用byte_4D738和byte_4D739这样独立的方式进行读取。
这是两种方式的读取,而采用哪种方式并没有太大的差别,这与程序的编译有关,编译中如果有的数据独立出来更好生成汇编代码就有可能采用独立的方式读取。

seg002:2A35 68 1A 55                       push    551Ah
seg002:2A38 9A 08 1D F6 1C                 call    sub_1EC68
seg002:2A3D 5B                             pop     bx
seg002:2A3E 8B D8                          mov     bx, ax
seg002:2A40 8E C2                          mov     es, dx
seg002:2A42 26 8A 07                       mov     al, es:[bx]            ;从ES{551A}:0读取地貌图的宽
seg002:2A45 D0 E8                          shr     al, 1                  ;除以2得出地形图的宽W
seg002:2A47 A2 68 CF                       mov     byte_4D738, al         ;保存到DS:CF68
seg002:2A4A 68 1A 55                       push    551Ah
seg002:2A4D 9A 08 1D F6 1C                 call    sub_1EC68
seg002:2A52 5B                             pop     bx
seg002:2A53 8B D8                          mov     bx, ax
seg002:2A55 8E C2                          mov     es, dx
seg002:2A57 26 8A 47 01                    mov     al, es:[bx+1]          ;从ES{551A}:1读取地貌图的高
seg002:2A5B D0 E8                          shr     al, 1                  ;除以2得出地形图的高H
seg002:2A5D A2 69 CF                       mov     byte_4D739, al         ;保存到DS:CF69

sub_1EC68函数在番外1就有简介了,提到申请内存的时候还会有详细说明。
注意seg002:2A45和seg002:2A5B的shr al, 1这句,这就是移位运算,al向右移1位,也就是al向下整除2。英杰传中有相当多的整除运算不保留小数,只能保留余数。shl是左移运算,相当于乘法。
整除2的原因是英杰传同一幅地图有两种宽和高,一个是常见的地形图,就是每个坐标代表着一种地形;另一种是地貌图,每一个坐标的地形由四块地貌组成,每一种地貌也对应着代码,这样就构成了形形色色的战场画面,显而易见地貌图的宽高都是地形图的两倍,总的大小是地形图的四倍。更多的说明可以参见龙吟的三国志英杰传战场地图文件分析

seg002:2C68 56                             push    si                    ;SI=CF6A
seg002:2C69 9A B4 3E F2 2C                 call    sub_30DD4
seg002:2C6E 32 C0                          xor     al, al
seg002:2C70 A2 08 2D                       mov     byte_434D8, al        ;DS:2D08
seg002:2C73 A2 09 2D                       mov     byte_434D9, al        ;DS:2D09
seg002:2C76 88 44 05                       mov     [si+5], al
seg002:2C79 88 44 01                       mov     [si+1], al

这段代码中的[si+5]和[si+1]就是表示关卡状态,其中[CF6A+5]比较简单,就是0一个状态1一个状态,就是在不操作的时候,部队也会变换动作,就是不停在0和1之间切换,但只是把[CF6A+5]改变并不能直接实现动画效果,这个相当于只是一个标志。

seg002:6B2A                sub_33A4A proc far
seg002:6B2A
seg002:6B2A                arg_0           = word ptr  6              ;arg_0=CF6A
seg002:6B2A
seg002:6B2A 55                             push    bp
seg002:6B2B 8B EC                          mov     bp, sp
seg002:6B2D 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:6B30 8A 47 05                       mov     al, [bx+5]
seg002:6B33 C9                             leave
seg002:6B34 CA 02 00                       retf    2
seg002:6B34                sub_33A4A endp



seg002:6B38                sub_33A58 proc far
seg002:6B38
seg002:6B38                arg_0     = word ptr  6                  ;arg_0=CF6A
seg002:6B38
seg002:6B38 55                             push    bp
seg002:6B39 8B EC                          mov     bp, sp
seg002:6B3B 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:6B3E 80 77 05 01                    xor     byte ptr [bx+5], 1     ;1与0异或(xor)为1,所以[bx+5]会在0和1之间不停切换
seg002:6B42 C9                             leave
seg002:6B43 CA 02 00                       retf    2
seg002:6B43                sub_33A58 endp

[CF6A+1]是整个关卡的状态代码,00=未结束 01=全军撤退 02=胜利(殘存部隊) 03=失败,具体是如何变换的就先不进行分析了。

seg002:4138                sub_31058 proc far
seg002:4138
seg002:4138                arg_0     = word ptr  6                 ;arg_0=CF6A
seg002:4138
seg002:4138 55                             push    bp
seg002:4139 8B EC                          mov     bp, sp
seg002:413B 8B 5E 06                       mov     bx, [bp+arg_0]
seg002:413E 8A 47 01                       mov     al, [bx+1]
seg002:4141 C9                             leave
seg002:4142 CA 02 00                       retf    2
seg002:4142                sub_31058 endp

顶部
性别:未知-离线 漫漫苦短

Rank: 1
组别 百姓
级别 在野武将
功绩 0
帖子 42
编号 545816
注册 2023-12-25


发表于 2025-2-18 19:19 资料 短消息 只看该作者
三、关卡名称和过关经验值(50点)

其实除了上面介绍CF6A的一些组成部分,包括了DS:CF6A-DS:CF6F(CF6A+5)的地址空间,在CF6A+6和后面有N字节部分,保存着现在进行的关卡名称。

seg002:2CBD 56                             push    si                 ;si=CF6A
seg002:2CBE 9A 04 3F F2 2C                 call   sub_30E24               ; 保存关卡名称到DS:CF70中
...
seg002:2CF8 6A 10                          push    10h
seg002:2CFA 6A 20                          push    20h
seg002:2CFC 68 A0 01                       push    1A0h
seg002:2CFF 68 60 01                       push    160h
seg002:2D02 68 90 CF                       push    0CF90h
seg002:2D05 9A CE 45 F2 2C                 call    sub_314EE

这里发现调用了两个函数,上面的那个函数就是保存关卡名字,下面的就是在战役开始之前初始化整个节目,其中就包括在最上方输出的那个关卡名称。

seg002:3F04                sub_30E24       proc far                ; CODE XREF: sub_2FB7A+64↑P
seg002:3FC4 83 C1 06                       add     cx, 6                 ;cx=CF6A
seg002:3FC7 1E                             push    ds
seg002:3FC8 51                             push    cx                       ;此处是关卡名称字符串的首地址DS:CF70
seg002:3FC9 1E                             push    ds
seg002:3FCA 68 81 30                       push    offset asc_43851                        ; "之戰"
seg002:3FCD 9A B8 3C F6 1C                 call    sub_20C18                          ;字符串连接,关卡名称后面加上之"之戰"
seg002:3FD5                sub_30E24       endp

这个字符串连接(Concat)函数可以说是一个不起眼但在后面的分析中会经常用到的,大部分的程序中保存字符串都是占用一段连续的空间,最后以00(不是数字0)作为结尾,而要想将两个不同地址的字符串拼接在一起,就要用到这样的函数,比如说一个字符串占用DS:OFF1的地址长度为a,占用空间DS:OFF1-DSOFF1+a-1),另一个字符串占用DS:OFF2的地址长度为b,两者合并在一起就会占用空间DS:OFF1-DSOFF1+a+b-1),这样的作用就是可以一起输出两个字符串,而不用考虑分别放在什么位置。

seg002:45CE                sub_314EE       proc far                ; CODE XREF: sub_2FB7A+AB↑P
seg002:46D7 68 6A CF                       push    0CF6Ah
seg002:46DA 9A 2A 41 F2 2C                 call    sub_3104A                           ;获取关卡名称字符串的首地址
seg002:46DF 50                             push    ax
seg002:46E0 68 23 31                       push    offset asc_438F3                  ; "%s"
seg002:46E3 68 26 51                       push    5126h
seg002:46E6 9A 3E 0C F6 1C                 call    sub_1DB9E                   ; 显示战役名字
seg002:46EB 83 C4 06                       add     sp, 6
seg002:4790                sub_314EE       endp



seg002:412A               sub_3104A proc far
seg002:412A
seg002:412A                arg_0     = word ptr  6            ;arg_0=CF6A
seg002:412A
seg002:412A 55                             push    bp
seg002:412B 8B EC                          mov     bp, sp
seg002:412D 8B 46 06                       mov     ax, [bp+arg_0]
seg002:4130 05 06 00                       add     ax, 6
seg002:4133 C9                             leave
seg002:4134 CA 02 00                       retf    2
seg002:4134               _sub_3104A endp

从DS:[CF6A+6]到DS:[CF6A+23]中都是关卡名称的内存空间,有种想法是不同关卡的名字长度都不同,那为啥不动态调整它们所占内存的长度,而是要固定其中的长度,可能有以下原因,一是在静态语言编写的程序中,都是要向内存申请固定长度的空间,因为这样方便程序的管理,二是动态调整的话有可能会影响到其他数据块的地址,固定长度更加方便。

DS:[CF6A+24]就是新的数据内容了,它就是每关的过关经验值。

seg002:3BE6                sub_30B06       proc far                ; CODE XREF: sub_30C28+14C↓P
seg002:3BE6
seg002:3BE6                var_4           = word ptr -4
seg002:3BE6                var_1           = byte ptr -1
seg002:3BE6                arg_0           = word ptr  6            ;arg_0=CF6A
seg002:3BE6
seg002:3BE6 C8 04 00 00                    enter   4, 0
seg002:3BEA 57                             push    di
seg002:3BEB 56                             push    si
seg002:3BEC 8B 7E 06                       mov     di, [bp+arg_0]
seg002:3BEF 80 7D 01 02                    cmp     byte ptr [di+1], 2            ;这个就是前面介绍的关卡状态
seg002:3BF3 75 77                          jnz     short loc_30B8C
seg002:3BF5 80 7D 24 00                    cmp     byte ptr [di+24h], 0          ;判断是否有过关经验值,0表示没有
seg002:3BF9 74 14                          jz      short loc_30B2F
seg002:3BFB 8A 45 24                       mov     al, [di+24h]
seg002:3BFE 2A E4                          sub     ah, ah
seg002:3C00 50                             push    ax
seg002:3C01 1E                             push    ds
seg002:3C02 68 09 30                       push    offset asc_437D9              ;"殘存部隊得到\x1BC6%w\x1BC7點經驗值."
seg002:3C05 6A 01                          push    1
seg002:3C07 9A 42 32 F6 1C                 call    sub_201A2                    ;弹出获得经验值的提示框
seg002:3C0C 83 C4 08                       add     sp, 8

关于这个过关经验值是怎么设置的,以及过关经验值是如何加在每只部队上的,这里就先不细述了。

总结,关于CF6A的数据块,以及其中的成员就先概述这么多了,总之如果把这个数据块想象成一个家族,那么这个家族只有一个老大,就是CF6A这个地址,要想访问其中的成员,要通过CF6A来操作,而且其中的成员不多,关系也不复杂,接下来要介绍的可能就要复杂多了。

[ 本帖最后由 漫漫苦短 于 2025-2-18 19:52 编辑 ]
顶部

正在浏览此帖的会员 - 共 1 人在线




当前时区 GMT+8, 现在时间是 2025-4-2 05:47
京ICP备2023018092号 轩辕春秋 2003-2023 www.xycq.org.cn

Powered by Discuz! 5.0.0 2001-2006 Comsenz Inc.
Processed in 0.012630 second(s), 8 queries , Gzip enabled

清除 Cookies - 联系我们 - 轩辕春秋 - Archiver - WAP