Sunday, September 13, 2009

让引擎适应游戏的杂事

让引擎适应游戏是一件很麻烦的事情。
据说微软曾经为了一个游戏《猎鹿人》运行不了就推迟了Windows XP SP2一个测试版本的发布。当然我们这些做山寨游戏的不能跟微软比。如果你看过《Windows编程启示录》(这本书的名字听起来很高深,但纯粹是一本八卦集锦)的话,你也能体会到微软很多时候的无奈。其实Windows不是那么糟糕的系统,把它搞乱的是那些糟糕的软件。

1. 《苍龙逐日》的错误语句
看过复刻版事件部分源码的朋友应该知道我在3号指令那里写了一段注释:

//这里应该是原本z文件的bug, 如果不处于当前场景, 在连坐标值一起修改时, 并不会同时
//对S数据进行修改. 而<苍龙逐日>中有几条语句无意中符合了这个bug而造成正确的结果

这个错误实在很令人抓狂。因为它仅仅出现了三四次,而且全都是邪线。

2. 《再破菠萝》的跳转口
在内场景主循环中处理跳转口的语句是在《笑梦游记》之后才弄清楚的。原版处理跳转的逻辑实在是莫名其妙,如果能一切重新开始,那么我会选择用一个指令进行跳转,但是在复刻原版和MOD的时候,你必须去适应已有的事件。

    if ((sx = RScence[CurScence].JumpX1) and (sy = RScence[CurScence].JumpY1)) and (RScence[CurScence].JumpScence >= 0) then
    begin
      PreScence := CurScence;
      CurScence := Rscence[CurScence].JumpScence;
      if RScence[PreScence].MainEntranceX1 <> 0 then
      begin
        Sx := RScence[CurScence].EntranceX;
        Sy := RScence[CurScence].EntranceY;
      end
      else
      begin
        Sx := RScence[CurScence].JumpX2;
        Sy := RScence[CurScence].JumpY2;
      end;
      InitialScence;
      Drawscence;
      ShowScenceName(CurScence);
      CheckEvent3;
    end;

至今甚至很多制作人也没能完全弄清原版的逻辑。

3. 内存相关指令
50指令中的25和26都是内存相关指令,在内存的诸多应用被破译之后,这两个指令的相关事件也多了起来。而复刻版无疑是不能支持这两个指令的。
源码中50指令的25和26部分都是特殊处理,在26指令中有一个类似下面的列表:

          $1D295E: x50[e5] := CurScence;
          $1D295A: x50[e5] := Sx;
          $1D295C: x50[e5] := Sy;
          $1C0B88: x50[e5] := Mx;
          $1C0B8C: x50[e5] := My;
          //$1D2956: x50[e5] := Cx;
          //$1D2958: x50[e5] := Cy;
          $05B53A: x50[e5] := 1;
          $0544F2: x50[e5] := Sface;
          $1E6ED6: x50[e5] := x50[28100];
          $556DA: x50[e5] := Ax;
          $556DC: x50[e5] := Ay;

而在25指令中,情况更加复杂,因为部分指令是直接读取内存获得物品的信息。

4. 特殊子程
在没有复刻版之前,一些特殊功能的子程是通过带参数的50 43来实现的。其中最重要的应用是新的获取物品和新的对话。
在测试《笑梦游记》的复刻的时候,我发现跳转时代的执行效率非常低,后来经调试锁定问题在新的获取物品指令上。这个指令似乎是直接读内存,在DOS版中效率很高,但是在复刻版中由于执行机制变得效率极低。后来我在43指令中添加了一个列表,即如果发现要执行的子程是“获取物品”,就直接用2号指令代替,因为2号指令我扩充过功能,更加强于50指令编写的事件,更重要的是效率更高。
在猪3中的这个列表似乎更复杂了。


5. 直接执行内部子程
这个就是50 49指令。我认为在我添加的9个50指令中,49指令是最伟大的(吹吹牛)。而在我的说明书中的那个任意转换场景的例子,实际上包含了非常复杂的汇编原理。
但是相比25、26和43,49指令是绝对没可能移植的,甚至给出一个列表也是非常麻烦,因为参数的个数就是很棘手的问题。所以如果你的MOD真的使用了50 49指令,那么在不修改事件的前提下进行移植,基本上是不可能完成的任务。
所幸50 49还未传开。
下面就是50 49的源码,以此记念这个特殊时期的产物。

; 调用系统子程
_instruct_32_31_CallProcedure proc near ; DATA XREF: dseg02:0005D0C8o
pusha
mov     eax, [esp+20h+arg_4]
shl     eax, 10h
xor     ebx, ebx
mov     bx, [esp+20h+arg_0]
add     eax, ebx
add     eax, (offset set_textmode-20000h)
mov     ebp, eax
mov     ecx, [esp+20h+arg_8]
mov     esi, [esp+20h+arg_C]
imul    edi, esi, 4
cmp     esi, 0
jz      short loc_5E6A8
loc_5E698:
movsx   eax, ds:word_120000[ecx*2] ; 变量空间
push    eax
inc     ecx
dec     esi
cmp     esi, 0
jnz     short loc_5E698
loc_5E6A8:
call    ebp
add     esp, edi
mov     ecx, [esp+24h+arg_C]
mov     ds:word_120000[ecx*2], ax ; 变量空间
popa
retn

No comments: