冰凌汇编

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 63|回复: 0
收起左侧

[原创] ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程

[复制链接]
bigbang 发表于 2022-7-10 14:23:14

波可龙迷宫是一款日本的策略游戏,已经开服8年了,一款手游能活那么久实属少见,既然能存活那么久就有它热门的道理,如果没人玩早就倒闭了。软件下载地址:日服波可龙迷宫

------分割线------

人気急上昇中のなぞるパズルのRPGが満を持して登場!
スマホ·タブレットのために生み出されたパズルRPGを体感しよう!

ドラゴンなどのモンスターたち、仕掛け満載の多彩なダンジョン、カンタン操作で爽快バトル!
「ポコロンダンジョンズ(ポコダン)」はダウンロード無料でお楽しみいただけるパズルRPGです。

准备工具:

IDA32

软件基于当前最新版9.3.0,理论上即使版本更新也不会有太大的改变。这个教程其实很早的时候就想发了,只是有个修改方案会引起闪退问题,一直懒得找原因,所以就没发出来。最近应群里要求才修复了闪退bug,所以就发一下这个教程。
主要介绍如何秒过楼层,秒杀之类就不介绍了,容易被查封。

打开压缩包,查看lib内容,就2个so,修改目标就是libcocos2dcpp,ida加载它,加载完毕后打开输出表,修改方法比较简单,直接搜索一些关键字就可以了,比如gethp,def之类的,都是手游比较常见的代码。
ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程 - bigbang_冰凌汇编

比如我搜索gethp(读取血量),红色箭头是比较有用的信息,双冒号前面代表对象,玩家数据,怪物逻辑等。记录下来,PlayerData::如果要修改攻击力、防御力等玩家数据就搜索这个开头,EnemyMonsterLogic::同理。具体怎么修改比较简单,略过。
ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程 - bigbang_冰凌汇编

来看一下怎么直接过关,游戏界面如上图。

按照一般的设计,过关肯定是怪物都被打死了,才能前往下一层,那么修改方案就从这里入手,让怪物的HP固定为0。刚开始我搜索了一些关键词,比如::getIsDead,直接返回1就是默认怪物已经处于死亡状态,随便走一格就可以过关了,但是后来发现在某些副本会闪退。比如有的怪物有复活能力的,需要打两次才能过关,如果直接改成死亡,那么第一次移动的时候就会引起复活逻辑错误,接着就是闪退了,要么卡死。所以改这里并不是很好。
但是要修复这个问题,只能修改函数逻辑,锁定数值并不能解决问题,因为这个是逻辑错误引起的。比如你修改怪物的HP,改成0,第一次是没问题的,但是第二次打的时候复活需要增加HP,结果加不进去,就闪退了。

修复卡死闪退问题我是从死亡判断入手的,最根本的还是怪物血量问题,代码应该会有一个循环。刚才说到怪物HP为0会死亡,双击EnemyMonsterLogic::getIsDead进入,查看引用。
ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程 - bigbang_冰凌汇编

有一大堆,没办法,我一个个点进去看,其实比较容易排除,像一些缺少循环条件的肯定不是了,然后我又注意到一个函数EnemyMonstersManager::dieEnemyMonstersZeroHPs,有HP关键字,也有die死亡关键字,也有while,进入查看后基本上可以确实是这里引起了错误。

伪代码如下

int __fastcall EnemyMonstersManager::dieEnemyMonstersZeroHPs(EnemyMonstersManager *this)
{
  int v1; // r0
  int *v2; // r1
  EnemyMonsterLogic **v3; // r4
  char v4; // r10
  unsigned int v5; // r6
  EnemyMonsterLogic *v6; // r5
  int v7; // r3
  int v8; // r0
  int v9; // r1
  int v10; // r1
  int *v11; // r0
  EnemyMonsterLogic **v12; // r4
  unsigned int v13; // r5
  EnemyMonsterLogic *v14; // r0
  EnemyMonsterLogic **v15; // r2
  int v16; // r0
  int i; // r2
  int v18; // r8
  int v19; // r9
  int j; // r6
  EnemyMonsterLogic *EnemyMonsterLogicByRepresentationCell; // r0
  int hasPassiveSkill; // r0
  int *v23; // r4
  int v24; // r6
  EnemyMonsterLogic *v25; // r5
  EnemyMonsterData ***v26; // r4
  int v27; // r6
  EnemyMonsterData **v28; // r5
  QuestLogicManager *Instance; // r0
  int v30; // r5
  char *v31; // r1
  cocos2d::__NotificationCenter *v32; // r0
  int v33; // r4
  char *v34; // r1
  cocos2d::__NotificationCenter *v36; // [sp+8h] [bp-C8h]
  int v37; // [sp+10h] [bp-C0h]
  EnemyMonsterLogic **v38; // [sp+14h] [bp-BCh]
  int v39; // [sp+18h] [bp-B8h]
  int v41; // [sp+24h] [bp-ACh] BYREF
  int v42; // [sp+28h] [bp-A8h]
  int v43; // [sp+2Ch] [bp-A4h]
  char v44; // [sp+33h] [bp-9Dh] BYREF
  EnemyPassiveSkillMasterData *v45[2]; // [sp+34h] [bp-9Ch] BYREF
  EnemyMonsterLogic *v46; // [sp+3Ch] [bp-94h] BYREF
  unsigned __int8 v47; // [sp+43h] [bp-8Dh] BYREF
  EnemyMonsterData ***v48; // [sp+44h] [bp-8Ch] BYREF
  int v49; // [sp+48h] [bp-88h]
  int v50; // [sp+4Ch] [bp-84h]
  EnemyMonsterLogic **v51; // [sp+50h] [bp-80h] BYREF
  int v52; // [sp+54h] [bp-7Ch]
  int v53; // [sp+58h] [bp-78h]
  int *v54; // [sp+5Ch] [bp-74h] BYREF
  int v55[2]; // [sp+60h] [bp-70h] BYREF
  int v56[12]; // [sp+68h] [bp-68h] BYREF
  int v57[14]; // [sp+98h] [bp-38h] BYREF

  v55[0] = 0;
  v55[1] = 0;
  v54 = v55;
  v1 = *(this + 6);
  if ( !v1 )
  {
    v1 = 0;
    goto LABEL_13;
  }
  v2 = *(v1 + 24);
  if ( *v2 < 1 )
  {
LABEL_13:
    v9 = 0;
    goto LABEL_14;
  }
  v3 = v2[2];
  v4 = 0;
  v5 = &v3[*v2 - 1];
  while ( v3 <= v5 )
  {
    v6 = *v3;
    if ( !*v3 )
      break;
    if ( !EnemyMonsterLogic::getIsDead(*v3) )
    {
      if ( EnemyMonsterLogic::getHP(v6) < 1 || (EnemyMonsterLogic::isClearPointZero(v6), v8) )
      {
        sub_1083834(v56, &v54, *(v6 + 26) + 40, v7);
        v4 = 1;
        ++*(this + 10);
      }
    }
    ++v3;
  }
  v9 = v4 & 1;
  v1 = *(this + 6);
LABEL_14:
  v36 = v9;
  v10 = 0;
  v53 = 0;
  v51 = 0;
  v52 = 0;
  if ( v1 )
  {
    v11 = *(v1 + 24);
    if ( *v11 >= 1 )
    {
      v12 = v11[2];
      v13 = &v12[*v11 - 1];
      while ( v12 <= v13 )
      {
        v14 = *v12;
        if ( !*v12 )
          break;
        v56[0] = *v12;
        if ( !EnemyMonsterLogic::getIsDead(v14) )
          sub_13F5258(&v51, v56);
        ++v12;
      }
      v15 = v51;
      v10 = v52;
      goto LABEL_25;
    }
    v10 = 0;
  }
  v15 = 0;
LABEL_25:
  v50 = 0;
  v48 = 0;
  v49 = 0;
  v47 = 0;
  v37 = v10;
  while ( v15 != v10 )
  {
    v38 = v15;
    v46 = *v15;
    if ( EnemyMonsterLogic::getHP(v46) < 1 || (EnemyMonsterLogic::isClearPointZero(v46), v16) )
    {
      EnemyMonsterLogic::die(v46);
      sub_13F5258(&v48, &v46);
      if ( EnemyMonsterData::hasEnemyPassiveSkillId(*(v46 + 26)) )
      {
        EnemyMonsterData::hasPassiveSkillType(*(v46 + 26), 30);
        EnemyMonsterData::getPassiveSkill(v45, *(v46 + 26));
        v44 = 0;
        v43 = 0;
        v41 = 0;
        v42 = 0;
        sub_1036CB0(&v41);
        v57[4] = v57;
        v57[2] = &v41;
        v57[3] = &v47;
        v57[1] = &v44;
        v57[0] = &off_207B4EC;
        EnemyPassiveSkillMasterData::foreachAllData(v45[0]);
        sub_112D266(v57);
        if ( v44 )
        {
          for ( i = 0; i < **(*(this + 6) + 24); i = v39 + 1 )
          {
            v39 = i;
            v18 = *(*(*(*(*(this + 5) + 24) + 24) + 8) + 4 * i);
            if ( !GRAntiMemoryCheatBool::getData((v18 + 420)) )
            {
              v19 = sub_1BC6788(v42 - v41, 12);
              for ( j = 0; j < v19; ++j )
              {
                std::vector<std::string>::at(&v41, j);
                if ( (!sub_1043250() || !sub_1043250())
                  && GRAntiMemoryCheatIntTemplate<int>::getData(v18 + 112) >= 1
                  && (!EnemyMonsterLogic::hasClearPoint(v46) || EnemyMonsterLogic::getClearPoint(v46) >= 1) )
                {
                  ++*(this + 10);
                  EnemyMonsterData::getCell(v56);
                  EnemyMonsterLogicByRepresentationCell = EnemyMonstersManager::getEnemyMonsterLogicByRepresentationCell(
                                                            this,
                                                            v56);
                  EnemyMonsterLogic::die(EnemyMonsterLogicByRepresentationCell);
                }
              }
            }
          }
        }
        std::__vector_base<std::string>::~__vector_base(&v41);
        std::shared_ptr<EnemyPassiveSkillMasterData>::~shared_ptr(v45);
      }
    }
    v10 = v37;
    v15 = v38 + 1;
  }
  if ( *(this + 151) )
  {
    v36 = (&dword_0 + 1);
    hasPassiveSkill = 0;
    v47 = 1;
    *(this + 151) = 0;
  }
  else
  {
    hasPassiveSkill = v47;
    if ( !v47 )
    {
      hasPassiveSkill = EnemyMonstersManager::checkAndExecuteAllDieTogether(this, &v54);
      goto LABEL_56;
    }
  }
  v23 = v51;
  v24 = v52;
  while ( v24 != v23 )
  {
    v25 = *v23;
    hasPassiveSkill = EnemyMonsterLogic::hasPassiveSkill(*v23, 109);
    if ( !hasPassiveSkill )
    {
      hasPassiveSkill = EnemyMonsterLogic::getIsDead(v25);
      if ( !hasPassiveSkill )
      {
        EnemyMonsterLogic::die(v25);
        hasPassiveSkill = *(this + 10) + 1;
        *(this + 10) = hasPassiveSkill;
      }
    }
    ++v23;
  }
LABEL_56:
  v26 = v48;
  v27 = v49;
  while ( v27 != v26 )
  {
    v28 = *v26;
    Instance = QuestLogicManager::getInstance(hasPassiveSkill);
    if ( QuestLogicManager::isForceGameOver(Instance) )
      hasPassiveSkill = EnemyMonsterData::setDropBoardItemData(v28[26], 0);
    else
      hasPassiveSkill = EnemyMonstersManager::dropBoardItem(this, v28);
    ++v26;
  }
  if ( !v36 )
  {
    v30 = cocos2d::__NotificationCenter::getInstance(0);
    v31 = dword_229D9BC;
    if ( !(byte_229D9B4 << 31) )
      v31 = &unk_229D9B5;
    std::string::basic_string<decltype(nullptr)>(v56, v31);
    cocos2d::__NotificationCenter::postNotification(v30, v56, 0);
    std::string::~string(v56);
  }
  v32 = *(this + 10);
  if ( v32 >= 1 )
  {
    v33 = cocos2d::__NotificationCenter::getInstance(v32);
    v34 = dword_229E03C;
    if ( !(byte_229E034 << 31) )
      v34 = &unk_229E035;
    std::string::basic_string<decltype(nullptr)>(v56, v34);
    cocos2d::__NotificationCenter::postNotification(v33, v56, 0);
    std::string::~string(v56);
  }
  std::__vector_base<EnemyMonsterLogic *>::~__vector_base(&v48);
  std::__vector_base<EnemyMonsterLogic *>::~__vector_base(&v51);
  std::__tree<std::string>::~__tree(&v54);
  return _stack_chk_guard - v57[6];
}

关键部分为:

if ( EnemyMonsterLogic::getHP(v6) < 1 || (EnemyMonsterLogic::isClearPointZero(v6), v8) )

有2个这样的判断,其中EnemyMonsterLogic::getHP(v6) < 1是关键地方,很容易看出这是什么意思,当怪物HP小于1或者归零就过关。
我们可以修改这里的判断逻辑,小于改成大于等于就行了。

原指令BLT改成BGE即可,效果如下:
ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程 - bigbang_冰凌汇编

ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程 - bigbang_冰凌汇编

但是这样还有个问题,如果在移动过程中把怪物打死了,他还是会卡。因为改成大于等于1以后,是没有攻击怪物的情况下就正常,如果把怪物打死了,HP小于1,那么和原本的逻辑又有冲突了。所以还需要把下限改成0。
ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程 - bigbang_冰凌汇编

ポコロンダンジョンズ 波可龙迷宫 作弊 开挂 MOD教程 - bigbang_冰凌汇编

这样一来随便动一下即可过关,也可以正常捡箱子。

其它

PlayerData::getDEF:主角防御力。
EnemyMonsterLogic::getAccuracy:敌人命中率,修改任意值总是miss。
SummonableMonsterLogic::getSP:每走一格获得的sp,改200直接满。

冰凌汇编免责声明
以上内容均来自网友转发或原创,如存在侵权请发送到站方邮件9003554@qq.com处理。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|站点统计|Archiver|小黑屋|RSS|冰凌汇编 ( 滇ICP备2022002049号 滇公网安备 53032102000029号)|网站地图

GMT+8, 2022-10-6 22:00 , Processed in 0.151762 second(s), 14 queries , Redis On.

冰凌汇编 - 建立于2021年12月20日

Powered by Discuz! © 2001-2022 Comsenz Inc.

快速回复 返回顶部 返回列表