起因:
本人对剧情无感,但热衷于刷双打对战塔。
三代只有绿宝石有双打对战塔,而且与以后世代区别比较大。
四代开始双打基本稳定,后世代只是在四代的基础上添砖加瓦。
网上各路改版将绿宝石对战系统升级,并添加后世代宝可梦,非常符合我的口味,遂开始尝试各种改版刷对战塔。
但似乎所有升级了对战系统的改版都是在DizzyEgg的原始版本上加工而成,所有的版本都有相同的bug会导致死机。
Bug展示:
青铜钟用Trick Room(戏法空间),非必要,只为比对方先出手,樱花儿使用Protect(守住)

青铜钟成功开出戏法空

青铜钟使用Explosion(大爆炸)

樱花儿被炸死,青铜钟也挂了,对面两个都在

对面使用Surf(冲浪), 死机,樱花儿的血条又被显示出来了

尝试了数个改版,都是类似问题,非常不爽。
由于懒得分析gba反汇编代码,遂弃坑gba很长一段时间。
近日,机缘巧合之下在饭堂搜到一高质量绿宝石c语言重写版:https://game.pokefans.xyz/?p=824
源代码都有了,折腾吧。
按照INSTALL.md的步骤一步一步来,生成了pokeemerald.gba
一路打到对战塔,才发现居然有内置修改器
,早知道先分析代码了
赶紧尝试了一下双打,bug重现。
分析代码,问题出在NoTargetPresent()里:
字面意思,没有目标存在->有目标存在返回FALSE, 没有目标存在返回TRUE。
枚举变量MOVE_TARGET_FOES_AND_ALLY,字面意思,技能目标,敌人和盟友。
显然,冲浪会攻击到盟友,应该会跳入该代码段。
通过IsBattlerAlive()函数来判断目标,目标的盟友,和攻击者的盟友是否存活。
根据上面弄出bug的情况,这里应该返回FALSE(有目标存在),因为目标已死,目标盟友已死,攻击者的盟友存活。
乍看之下没啥问题,符合逻辑,接着找调用函数的地方:

if条件下有个显眼的函数CancelMultiTurnMoves(), 字面意思,取消多重攻击技能。
这里盟友还活着,返回FALSE不取消是对的。
问题出在接下来的地方用到了gBattlerTarget,我方双方都挂了,显然用哪个都不合适。
再看看NoTargetPresent()里,一开始会用IsBattlerAlive()来判断gBattlerTarget是否存活,如果挂了的话会把gBattlerTarget设为盟友。
问题就出在当攻击方使用全体攻击技能,对方都挂了,只有盟友存活的时候gBattlerTarget是个无效值。
好了,问题已经找到,修复如下:

使用全体攻击时,当目标双方都挂了,将gBattlerTarget设置成为盟友。
编译后重试,确认bug被修复,可以愉快的刷对战塔了!
思考:
绿宝石原版会有类似的问题吗?
由于绿宝石原版双打对战机制和后世代不同,当一方有人挂了,而场下还有替补存活的时候会直接上场,所以不存在这个问题,这个bug是由升级对战系统导致的。
尝试:
之前很火的究极绿宝石好像死机问题比较少,是不是修复了这个bug?
试了一下,究绿很明显发现了这个问题,但并没有修复,而是尽量去绕过这个bug: 敌人的对战技能里没有攻击全体的技能(至少我没发现), 最新版甚至连学大爆炸的途径都没有。
既然没修复,有好奇心的可以尝试去复现一下这个bug:
留敌人双方一点血,保证能被我方多人攻击技能一下打死,
我方先手攻击者用多人攻击技能把对面两个都带走
我方后手攻击者用全体技能(地震,冲浪,放电)攻击
看看能不能重现这个bug
本人对剧情无感,但热衷于刷双打对战塔。
三代只有绿宝石有双打对战塔,而且与以后世代区别比较大。
四代开始双打基本稳定,后世代只是在四代的基础上添砖加瓦。
网上各路改版将绿宝石对战系统升级,并添加后世代宝可梦,非常符合我的口味,遂开始尝试各种改版刷对战塔。
但似乎所有升级了对战系统的改版都是在DizzyEgg的原始版本上加工而成,所有的版本都有相同的bug会导致死机。
Bug展示:
青铜钟用Trick Room(戏法空间),非必要,只为比对方先出手,樱花儿使用Protect(守住)

青铜钟成功开出戏法空

青铜钟使用Explosion(大爆炸)

樱花儿被炸死,青铜钟也挂了,对面两个都在

对面使用Surf(冲浪), 死机,樱花儿的血条又被显示出来了

尝试了数个改版,都是类似问题,非常不爽。
由于懒得分析gba反汇编代码,遂弃坑gba很长一段时间。
近日,机缘巧合之下在饭堂搜到一高质量绿宝石c语言重写版:https://game.pokefans.xyz/?p=824
源代码都有了,折腾吧。
按照INSTALL.md的步骤一步一步来,生成了pokeemerald.gba
一路打到对战塔,才发现居然有内置修改器
赶紧尝试了一下双打,bug重现。
分析代码,问题出在NoTargetPresent()里:

字面意思,没有目标存在->有目标存在返回FALSE, 没有目标存在返回TRUE。
枚举变量MOVE_TARGET_FOES_AND_ALLY,字面意思,技能目标,敌人和盟友。
显然,冲浪会攻击到盟友,应该会跳入该代码段。
通过IsBattlerAlive()函数来判断目标,目标的盟友,和攻击者的盟友是否存活。
根据上面弄出bug的情况,这里应该返回FALSE(有目标存在),因为目标已死,目标盟友已死,攻击者的盟友存活。
乍看之下没啥问题,符合逻辑,接着找调用函数的地方:

if条件下有个显眼的函数CancelMultiTurnMoves(), 字面意思,取消多重攻击技能。
这里盟友还活着,返回FALSE不取消是对的。
问题出在接下来的地方用到了gBattlerTarget,我方双方都挂了,显然用哪个都不合适。
再看看NoTargetPresent()里,一开始会用IsBattlerAlive()来判断gBattlerTarget是否存活,如果挂了的话会把gBattlerTarget设为盟友。
问题就出在当攻击方使用全体攻击技能,对方都挂了,只有盟友存活的时候gBattlerTarget是个无效值。
好了,问题已经找到,修复如下:

使用全体攻击时,当目标双方都挂了,将gBattlerTarget设置成为盟友。
编译后重试,确认bug被修复,可以愉快的刷对战塔了!
思考:
绿宝石原版会有类似的问题吗?
由于绿宝石原版双打对战机制和后世代不同,当一方有人挂了,而场下还有替补存活的时候会直接上场,所以不存在这个问题,这个bug是由升级对战系统导致的。
尝试:
之前很火的究极绿宝石好像死机问题比较少,是不是修复了这个bug?
试了一下,究绿很明显发现了这个问题,但并没有修复,而是尽量去绕过这个bug: 敌人的对战技能里没有攻击全体的技能(至少我没发现), 最新版甚至连学大爆炸的途径都没有。
既然没修复,有好奇心的可以尝试去复现一下这个bug:
留敌人双方一点血,保证能被我方多人攻击技能一下打死,
我方先手攻击者用多人攻击技能把对面两个都带走
我方后手攻击者用全体技能(地震,冲浪,放电)攻击
看看能不能重现这个bug
最后编辑: