最终幻想OL(FF14)分析 - 基本数据

国庆在家闲来无事,下个了收费游戏,经典之作FF14,简单的分析了下,日本人的游戏结构挺简单。

具体的基地址就不写了,就发发实现的代码和效果


注意说明:

    1、游戏内的字符编码是UTF8

    2、游戏的数据结构大部分都是静态(至少看的几个都是)

    3、日本人的代码看起来比棒子的易懂(棒子是游戏做多了,框架封装的太好了)

一、背包列表

实现代码:

procedure ShowBagList();
var
  ItemPageBase:Cardinal;
  i,j,Count:Cardinal;
  BagObj:^_BagObj;
  Name:Pchar;
begin
  if pCardinal(g_BagBase)^ <> 0 then
    begin
      //有4页
      for i := 0 to 3 do
        begin
          ItemPageBase:= pCardinal(g_BagBase)^ + i * $18;
          if PCardinal(ItemPageBase)^ <> 0 then
            begin
              Printf('Item Page:%d',[i]);
              Count:= pInteger(ItemPageBase + 8)^;
              if Count > 0 then
                begin
                  for j := 0 to Count - 1 do
                    begin
                      BagObj:=Pointer(pCardinal(ItemPageBase)^ + j * $40);
                      if BagObj.ResId <> 0 then
                        begin
                          Name:=Pointer(g_GetItemNameByResId(BagObj.ResId) + $D0);
                          Printf('Idx:%d ResId:%X Name:%s Num:%d',[BagObj.Idx,BagObj.ResId,Utf8ToAnsi(Name),BagObj.Num]);
                        end;
                    end;
                end;
            end;
        end;
    end;
end;

二、环境列表

代码实现:

Procedure ShowWorld();
var
  ObjBase:^_RoleObj;
  I:Cardinal;
begin
  for I := 0 to $158 do
    begin
      ObjBase:=pPointer(g_WorldBase + i * 4)^;
      if ObjBase <> nil then
        begin
          Printf('[%d]Lv:%d Name:%s Hp:%d/%d Mp:%d/%d Pos:%.2f,%.2f,%.2f',[
          i,
          ObjBase.BaseInfo.Lv,
          Utf8ToAnsi(ObjBase.Name),
          ObjBase.BaseInfo.Hp,
          ObjBase.BaseInfo.MaxHp,
          ObjBase.BaseInfo.Mp,
          ObjBase.BaseInfo.MaxMp,
          ObjBase.x,
          ObjBase.z,
          ObjBase.y]);
        end;
    end;
end;

三、角色基本信息

实现代码:

Procedure ShowRoleInfo();
var
  RoleObj:^_RoleObj;
begin
  RoleObj:=pPointer(g_RoleBase)^;
  if RoleObj <> nil then
    begin
      Printf('Lv:%d',[RoleObj.BaseInfo.Lv]);
      Printf('Name:%s',[Utf8ToAnsi(RoleObj.Name)]);
      Printf('Hp:%d/%d',[RoleObj.BaseInfo.Hp,RoleObj.BaseInfo.MaxHp]);
      Printf('Mp:%d/%d',[RoleObj.BaseInfo.Mp,RoleObj.BaseInfo.MaxMp]);
      Printf('SkillPower:%d',[RoleObj.BaseInfo.SkillPower]);
      Printf('Pos:%.2f,%.2f,%.2f',[RoleObj.x,RoleObj.z,RoleObj.y]);
    end;
end;

=====================================我是分割线=====================================

这里要给出一个重要的对象结构体,上面的代码应该也能发现就是_RoleObj

  _RoleBaseInfo = Packed record
    ModIndex:Byte;
    Lv:Byte;
    Empty1:array [0..5] of byte;
    MaxHp,Hp:Cardinal;
    MaxMp,Mp:Cardinal;
    SkillPower:Word;
  end;

  _RoleObj = packed Record
    UnkonwMem1:array [0..$2F] of byte;  //  0x30
    Name:Array [0..$1F] of Char;        //  0x20
    UnkonwMem2:array [0..$4F] of byte;  //  0x50
    x,z,y:Single; Empty:Cardinal;
    Way:Single;
    Offset:Array [0..$177B] of Byte;    //  0x17E0 -> BASE + 0x1830
    BaseInfo:_RoleBaseInfo;
  End;

另外个背包的数据的结构,这个比较简单

  _BagObj = Packed Record
    Unkonw:Cardinal;
    Idx:Cardinal;
    ResId:Cardinal;
    Num:Cardinal;
  End;

最后不公开的几个函数和全局地址申明如下:

type
  TGetItemNameByResId = function (Resid:Cardinal):Cardinal;cdecl;
var
  g_RoleBase:Cardinal;
  g_BagBase:Cardinal;
  g_GetItemNameByResId:TGetItemNameByResId;  //通过资源ID查询物品名称
  g_WorldBase:Cardinal;

[吐槽]~~又是个挂B满天飞的游戏啊~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值