制作你的P5R模组
排版苦手,原文地址:P5R 模组制作笔记 - 飞书云文档
网站
Amicitia Wiki
最全面的P系列模组制作百科。包括游戏源文件解析,模组制作工具和ID表等。
Amicitia是ATLUS的JRPG的修改组织,特别是女神异闻录系列游戏。我们的目标是让粉丝尽可能多的进行自定义。在这里,您可以找到和编辑有关文件和工具的文档,并修改它们。
FearLess Cheat Engine
获取最新的CT文件。为了测试模组,CE和CT文件是不可或缺的。
Gamebanana
装载其他人的游戏模组,也可以在这里获取模组启动器Reloaded-II及其运行环境。
Reloaded II Documentation
了解Reloaded-II的使用方法。如果对本文中有关Reloaded-II的部分有疑问或操作时遇到困难,那么你可以浏览这个网站寻求解决方法。
工具
GARbro或CriPackGUI(或其他任意解包工具)
游戏档案文件解包工具,用以打开和提取CPK等格式的档案文件。
010 Editor
十六进制文本编辑器,用以编辑TBL等格式的数据文件。
世界上最好的十六进制编辑器!
为了轻松看懂十六进制文件,当然需要模板010 Editor Templates,下载保存到路径:
C:\Users\USERNAME\Documents\SweetScape\010 Templates\Repository
Cheat Engine
进程内存修改工具,用以测试模组。
其他工具
Persona5NameTableEditor
用以编译或反编译NAME.TBL. 可以修改游戏中项目的名称。
要修改或添加以下项目的名称,请下载本工具。
阿尔卡纳
消耗道具
重要道具
材料
近战武器
战斗行动
服装技能卡
人物简称
成员和协助人的姓氏
成员和协助人的名字
远程武器
AtlusScriptCompiler和它的图形用户界面(直接解压至前者的文件夹中即可使用)
用以编译或反编译,以及编辑英文的BF和BMD格式的文件。可以修改战斗中的敌人行动AI等。
AtlusScriptCompilerGUI简中版
用以编译或反编译,以及编辑简体中文的BF和BMD格式的文件。可以修改游戏中的文本信息等。
请一并下载网盘链接中的P5R汉字转编码&字库. 来源于B站大佬:奇Sim的爱。
PersonaEditor简中版
用以批量编辑BF和BMD格式的文件,并且可以编辑NAME.TBL. 可以修改游戏中的文本信息和游戏中项目的名称等。
来源于B站大佬:奇Sim的爱。
GFD Studio
用以查看和转换GMD和GAP格式的文件。可以修改游戏中的模型和模型的动画动作。
前言 如何使用模组
Reloaded-II
功能强大的游戏模组启动器。P5R几乎全部的模组都需要依赖它来运行。
手动安装:Reloaded-II的运行环境
.NET 7.0 Desktop Runtime (v7.0.0) - Windows x64 Installer.NET 7.0 SDK (v7.0.100) - Windows x64 Installer.NET 7.0 Runtime (v7.0.0) - Windows x64 Installer
如何使用Reloaded-II
将P5R添加到启动器
点击窗口左侧“⊕”图标(Add an Application), 找到P5R.exe选中并保存。

装载必需模组
点击窗口左上角“齿轮+”图标(Download Mods), 搜索Persona Essentials并下载。
装载本地模组文件
将模组压缩包解压到文件夹:
Reloaded-II\Mods
尽情享受吧!
点击窗口左上角“齿轮+”图标(Download Mods), 装载你想要的模组!
制作模组
前置工作:解包
以工具GARbro为例,对游戏档案文件BASE.CPK和SC.CPK进行解包。

对于解包出的文件,其目录列表、文件类型和打开方式都可以在Amiticia Wiki查看。
建议备份解包出的文件,并且不要修改备份文件。
修改技能:以“大罪穿甲弹”为例
修改技能数据
如果你仔细阅读过Amiticia Wiki里的文件列表,就会知道技能的数据存储在文件:
BASE\BATTLE\TABLE\SKILL.TBL
用010 Editor打开它。

为了能够简明的看懂这个文件,需要运行模板。
依次点击Tempaltes—>Open Templates—>p5r_tbl.bt, 然后再依次点击Tempaltes—>Run Templates—>p5r_tbl.bt—>Run, 在界面下方可以看到模板运行的结果。
如果Template Results没有出现,则用ALT+4查看。

显而易见,此文件分为四部分:
技能的元素
元素类型
主动/被动技能
继承等级:0表示不可继承的技能;相同继承等级的技能,变异时可以互相转变。
(最新版模板请忽略)注意!该部分的模板存在错误,模板中每个技能占3字节,实际上每个技能占8个字节。
主动技能的数据
魔法/物理技能
基础伤害
目标数
命中率
暴击率
……
该部分存储了除技能元素类型外主动技能的几乎全部数据。需格外关注。
未知的数据段
特性
详见修改特性:以“空前绝后的天性”为例。
了解模板运行结果的结构后,着手修改“大罪穿甲弹”的技能数据。
在Amiticia Wiki的技能表中,查找“大罪穿甲弹”(Sinful Shell)的ID,为0662.
(网页中使用CTRL+F查找关键字)
在Active Skill Data中找到Active Skill Data:[662], 即“大罪穿甲弹”的数据。

可以看到Base Damage的值为0,这就是为什么使用CE调出的”大罪穿甲弹”在游戏中实际伤害只有1.
Physical or Magic?的Comment有误,该项的值不决定技能是否被蓄力或专心致志影响,实际作用有待测试。而"Damage Stat"不仅决定技能被力或魔影响,而且决定技能被蓄力或是专心致志影响。
Base Damege不建议设置过大(超过30,000), 可能会发生越界错误,导致实际伤害变为1.
Crit Chance的Comment有误,万能魔法技能也可以暴击。
人格面具使用技能的动作由Damage Stat决定。以“撒旦耶尔”为例,如果Damege Stat的值为StrengthStat(1), 则该技能使用右手开枪动作,否则使用左手握拳动作。
此外,可对数据做出任意修改。
(最新版模板请忽略)扩展:修改技能元素类型
前文提到,在模板运行结果的第一部分Skill Elements中,模板存在错误。模板中每个技能占3字节(冗余位占1字节),实际上每个技能占8个字节(冗余位占6字节)。这就是为什么该部分足有2816项之多。
为了能够正确的修改技能元素类型,需要做一些准备工作,这里给出两种方法:
方法:找到数据的正确位置(急时抱佛脚)
因为模板存在错误,所以不能直接找Skill Elements[662].
右键界面左侧地址号,依次点击Addresses—>Byte number (Decimal), 将行号改为十进制显示。然后依次点击Addresses—>Set Starting Addresses,并将起始地址改为0.
右键界面上方列号,依次点击Line Width—>Fixed 4 Bytes, 将行宽度改为4字节。
注意到文件开头含有占4个字节的uint, 而你想要修改ID为622的技能,且每个技能的元素占8字节,所以要找第5300(4+8*662)字节。右键界面上方列号,点击Goto...,确保设置正确:

5300位决定技能元素类型,5301位决定主动/被动技能,5302-5307位是冗余位字段。

方法:更改冗余位字段定义(一劳永逸)
任意打开Skill Elements[x], 右键Redundant Bitfield, 点击Goto Definintion, 在弹出的P5R_TBL.bt中,将高亮行改为:
b48 RedundantBitfield_ThanksATLUS <name = "Redundant Bitfield", comment = "Rightmost 4 bits of the bitfield don't do anything. Probably leftovers from early P5 coding.">;
此时由于没有定义过结构b48, 需要定义b48, 在代码开头插入:
typedef struct { u8 bit0 : 1; u8 bit1 : 1; u8 bit2 : 1; u8 bit3 : 1; u8 bit4 : 1; u8 bit5 : 1; u8 bit6 : 1; u8 bit7 : 1; u8 bit8 : 1; u8 bit9 : 1; u8 bit10 : 1; u8 bit11 : 1; u8 bit12 : 1; u8 bit13 : 1; u8 bit14 : 1; u8 bit15 : 1; u8 bit16 : 1; u8 bit17 : 1; u8 bit18 : 1; u8 bit19 : 1; u8 bit20 : 1; u8 bit21 : 1; u8 bit22 : 1; u8 bit23 : 1; u8 bit24 : 1; u8 bit25 : 1; u8 bit26 : 1; u8 bit27 : 1; u8 bit28 : 1; u8 bit29 : 1; u8 bit30 : 1; u8 bit31 : 1; u8 bit32 : 1; u8 bit33 : 1; u8 bit34 : 1; u8 bit35 : 1; u8 bit36 : 1; u8 bit37 : 1; u8 bit38 : 1; u8 bit39 : 1; u8 bit40 : 1; u8 bit41 : 1; u8 bit42 : 1; u8 bit43 : 1; u8 bit44 : 1; u8 bit45 : 1; u8 bit46 : 1; u8 bit47 : 1; } b48;
CTRL+S保存后,重新运行模板。如果Skill Elements变成了1056个,说明修改正确。此时Skill Elements[662]即为“大罪穿甲弹”的元素类型。
修改技能粒子效果
技能的粒子效果存储在文件:
BASE\BATTLE\SKILL\0xxx.BED
SKILL文件夹并没有0662.BED文件,即“大罪穿甲弹”没有粒子效果。可以用其他技能的粒子效果替代。
例如,使用一枪毙命(ID:0226)的粒子效果。将0226.BED复制粘贴并重命名为0662.BED.
修改技能音效
技能的音效存储在文件:
BASE\SOUND\SKILLSE\0xxx.ACB
与替换粒子效果同理,将0226.ACB复制粘贴并重命名为0662.ACB.
至此,你已经成功修改了“大罪穿甲弹”的数据,并使用了一枪毙命的粒子效果和音效。
修改特性:以“空前绝后的天性”为例
特性数据和技能数据存储在相同的文件:
BASE\BATTLE\TABLE\SKILL.TBL
用010 Editor打开并运行模板。打开第四部分的任意特性,发现其构成如下:

因为我对模板做了些许修正,所以你看到的名称可能与图片中略有不同。不过影响不大,还是可以照常修改。你也可以下载并运行我修改后的模板,请直接覆盖原来的模板之后再运行。
效果与触发的条件:每个特性有独特的数值。
触发的概率
子特性:该特性可以发动数值所指向的子特性的效果。
如:特性ID136为MONA第三次觉醒面具的特性“威风凛凛的风范”:增加友方的治疗技能的效果,并偶尔减少其SP消耗。
这实际上是两个效果,前者是其子特性ID135“清高的风范”的效果,后者才是“威风凛凛的风范”的原本效果。
换言之,子特性功能可以使特性叠加。
效果的倍率
合成新人格面具时的额外继承特性:只对宝魔特性生效。
特性的类别:普通特性为0或4,DLC即不可继承特性为1或5,宝魔特性为2.
现在着手修改“空前绝后的天性”。
在Amiticia Wiki中,查找“空前绝后的天性”(Eccentric Temper)的ID,为133.
可以看到,其效果为:有25%的概率使友方物理技能伤害变为1.8倍。
选择s32 effect_rate, 修改为100;
选择s32 effect_size, 修改为3.
至此,“空前绝后的天性”的效果已修改为:使友方物理技能伤害变为3倍.
修改人格面具:以“撒旦耶尔”为例
人格面具的数据存储在文件:
BASE\BATTLE\TABLE\PERSONA.TBL
用010 Editor打开并运行模板。发现此文件由四部分构成:
人格面具的数值
阿尔卡纳类别
基础等级
基础数值
技能继承类型
技能和数值成长
数值成长权重分配
技能
学习该技能需要提升的等级
是否可学习
技能ID
队伍成员等级提升所需经验的阈值
队伍成员的人格面具
角色
可达到的等级
人格面具技能
升级获得的人格面具数值
现在着手修改“撒旦耶尔”。
在Amiticia Wiki中,查找“撒旦耶尔”(Satanael)的ID,为0170.
在模板运行结果的Skills & Stat Growths找到Skills & Stat Growths[170], 选择Skill[9], 修改为:

你的“撒旦耶尔”会在99级学会“大罪穿甲弹”。
如果你想修改人格面具的元素相性(属性抗性),请参考下文:修改敌方单位:以“猎杀者”为例的前半部分。
修改敌方单位
修改敌人数据:以“猎杀者”为例
关于单位的数据存储在文件:
BASE\BATTLE\TABLE\UNIT.TBL
打开并运行模板。这里只看前三部分:
敌方单位的数值
阿尔卡纳类别
等级
HP和SP
数值(五维)
战斗技能
经验和金钱奖励
攻击伤害
……
敌人的元素相性
人格面具的元素相性
第一部分的战斗技能(Battle Skills)中,只有设置的自动技能会生效,而敌人使用的主动技能由AI文件决定。
修改敌人的行动AI:以“LV.???自称特别搜查队的少年”为例
敌人的AI数据存储在ELSAI.TBL中。

如果某敌人的AI ID为0,则它只会使用AICAIL.TBL中对应的技能;如果AI ID为xxxx, 则它的AI数据存储在文件:
BASE\BATTLE\SCRIPT\ENEMY\BTL_AIxxxx.BF
前者毫无intelligence可言。于是我们重点关注BTL_AIxxxx.BF文件。
现在着手修改“LV.???自称特别搜查队的少年”的行动AI。
关于BF格式文件的打开方式
“LV.???自称特别搜查队的少年”的AI文件为BTL_AI0745.BF, 使用原版AtlusScriptCompilerGUI.exe反编译。对于生成的BTL_AI0745.BF.flow. 可以用记事本打开和编辑它。
番长的AI代码
// // FlowScript decompiled using Atlus Script Tools by TGE (2017-2021) // In the unfortunate case of any bugs, please report them back to me. // // // Procedure declarations // // Procedure Index: 0 void AI_MAIN() { int var0; BIT_ON( 11550 ); var0 = GET_COUNT( 308 ); //将var0定义为GET_COUNT( 308 ), 这是切换人格面具的函数 if ( AI_CHK_MORE() == 1 ) //ONE MORE时 PUTS( "1more" ); //发动ONE MORE if ( var0 == 452 ) //人格面具为伊邪纳岐时 { AI_SET_GLOBAL( 0, 0 ); //将全局变量0的值设置为0,全局变量0用以表示该轮的回合数 AI_ACT_SKILL( 705 ); //行动:使用技能十字文斩 AI_TAR_RND(); //目标:随机 } else if ( var0 == 457 ) //人格面具为黄龙时 { AI_SET_GLOBAL( 0, 1 ); AI_ACT_SKILL( 337 ); //行动:使用技能斯库卡加 AI_TAR_MINE(); //目标:自身 } else if ( var0 == 458 ) //人格面具为辉夜时 { AI_SET_GLOBAL( 0, 2 ); AI_ACT_ATTACK(); //行动:攻击 AI_TAR_RND(); //目标:随机 } else if ( var0 == 459 ) //人格面具为斯拉欧加时 { AI_SET_GLOBAL( 0, 3 ); AI_ACT_SKILL( 371 ); //行动:使用技能玛卡拉康 AI_TAR_MINE(); //目标:自身 } else if ( var0 == 460 ) //人格面具为义经时 { AI_SET_GLOBAL( 0, 4 ); AI_ACT_SKILL( 338 ); //行动:使用技能灼热奋起 AI_TAR_MINE(); //目标:自身 } else if ( var0 == 461 ) //人格面具为伊邪纳岐大神时 { AI_SET_GLOBAL( 0, 4 ); AI_ACT_SKILL( 338 ); //行动:使用技能数万真言 AI_TAR_MINE(); //目标:自身(群体技能目标为自身和目标为随机是等效的) } else { AI_ACT_ATTACK(); //行动:攻击 AI_TAR_RND(); //目标:随机 } return; } //切换人格面具的元素相性,并更改本轮的回合数 if ( var0 == 461 ) //人格面具为伊邪纳岐大神时 { SET_COUNT( 308, 452 ); //COUNT( 308 )的值设置为452 AI_SET_AFFINITY( 452 ); //元素相性设置为伊邪纳岐 AI_SET_GLOBAL( 0, 1 ); //全局变量0的值设置为1,本轮的第一回合 } else if ( var0 == 452 ) //人格面具为伊邪纳岐时 { SET_COUNT( 308, 457 ); AI_SET_AFFINITY( 457 ); AI_SET_GLOBAL( 0, 2 ); } else if ( var0 == 457 ) //人格面具为黄龙时 { SET_COUNT( 308, 458 ); AI_SET_AFFINITY( 458 ); AI_SET_GLOBAL( 0, 3 ); } else if ( var0 == 458 ) //人格面具为辉夜时 { SET_COUNT( 308, 459 ); AI_SET_AFFINITY( 459 ); AI_SET_GLOBAL( 0, 4 ); } else if ( var0 == 459 ) //人格面具为斯拉欧加时 { SET_COUNT( 308, 460 ); AI_SET_AFFINITY( 460 ); AI_SET_GLOBAL( 0, 5 ); } else if ( var0 == 460 ) //人格面具为义经时 { SET_COUNT( 308, 461 ); AI_SET_AFFINITY( 461 ); AI_SET_GLOBAL( 0, 5 ); } else { SET_COUNT( 308, 452 ); AI_SET_AFFINITY( 452 ); AI_SET_GLOBAL( 0, 0 ); } var0 = GET_COUNT( 308 ); //不同轮次、不同人格面具的行动 if ( ( AI_GET_LOCAL_PARAM( 0 ) == 0 ) && ( var0 == 452 ) ) //AI_GET_LOCAL_PARAM( 0 )用来获得本地参数0,这是获得轮次的函数 { AI_ACT_SKILL( 705 ); AI_TAR_ID( 1 ); //目标:JOKER } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 1 ) && ( var0 == 452 ) ) { AI_ACT_SKILL( 151 ); AI_TAR_RND(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 0 ) && ( var0 == 457 ) ) { AI_ACT_SKILL( 278 ); AI_TAR_RND(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 1 ) && ( var0 == 457 ) ) { AI_ACT_SKILL( 97 ); AI_TAR_RND(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 0 ) && ( var0 == 458 ) ) { AI_ACT_SKILL( 708 ); AI_TAR_RND(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 1 ) && ( var0 == 458 ) ) { AI_ACT_SKILL( 708 ); AI_TAR_RND(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 0 ) && ( var0 == 459 ) ) { AI_ACT_SKILL( 131 ); AI_TAR_RND(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 1 ) && ( var0 == 459 ) ) { AI_ACT_SKILL( 360 ); AI_TAR_MINE(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 0 ) && ( var0 == 460 ) ) { if ( AI_CHK_ENBAD( 2 ) == 1 ) //当有敌人陷入冰冻状态时 { AI_ACT_ATTACK(); AI_TAR_BAD( 2 ); //目标:陷入冰冻状态的敌人 } else { AI_ACT_ATTACK(); AI_TAR_RND(); } } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 1 ) && ( var0 == 460 ) ) { AI_ACT_SKILL( 215 ); AI_TAR_RND(); } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 0 ) && ( var0 == 461 ) ) { AI_ACT_SKILL( 713 ); AI_TAR_RND(); AI_SET_LOCAL_PARAM( 0, 1 ); //本轮结束,将轮次改为1 } else if ( ( AI_GET_LOCAL_PARAM( 0 ) == 1 ) && ( var0 == 461 ) ) { AI_ACT_SKILL( 713 ); AI_TAR_RND(); AI_SET_LOCAL_PARAM( 0, 0 ); //本轮结束,将轮次改为0 } else { AI_ACT_ATTACK(); AI_TAR_RND(); } }
其直观表示为:
使用的技能
使用的人格面具
伊邪纳岐
黄龙
辉夜
斯拉欧加
义经
伊邪纳岐大神
ONE MORE
十字文斩
斯库卡加
攻击
玛卡拉康
灼热奋起
数万真言
第一轮
十字文斩
碎脑击
辉箭
大冰河时期
攻击
数万真言
第二轮
崇高圣战
精神污染
辉箭
蓄力
八艘跳跃
数万真言
第三轮及以后
重复第一轮及第二轮
AI函数
以下列出部分重要AI函数的伪代码定义。要想浏览P5R所有函数的信息,请在AtlusScriptCompiler的文件目录中找到Modules文件夹:
Libraries\Persona5Royal\Modules
其各文件夹下的Functions.json中包含P5R所有函数的信息。
AI_SET函数:设置参数
void AI_SET_FRID_MAXSERIAL( EnemyID, int x ){ 设置EnemyID的每回合行动数为x; //EnemyID应与AI文件的ID一致 } void AI_SET_GLOBAL( int x, int y){ //y < 0x10 全局变量x = y; } void AI_SET_LOCAL_PARAM( int x, int y ){ //y < 0x10 局部参数x = y; } void AI_SET_AFFINITY( EnemyID ){ 自身的元素相性 = EnemyID的元素相性; }
AI_ACT函数:行动
void AI_ACT_SKILL( SkillID ){ 使用技能SkillID; } void AI_ACT_ATTACK(){ 攻击; } void AI_ACT_TAKEOVER( EnemyID ){ 换手给EnemyID; } void AI_ACT_WAIT(){ 等待; } void AI_ACT_WAIT3( SkillID ){ 等待使用SkillID; }
AI_TAR函数:选取目标
return AI_TAR_RND(){ 目标 = 随机; //使用群体技能时即以群体为目标 return 0; } void AI_TAR_MINE(){ 目标 = 自身; } void AI_TAR_DOWN(){ 目标 = 倒地的目标; } void AI_TAR_ID( MemberID or EnemyID ){ 目标 = ID为MemberID或EnemyID的目标; } void AI_TAR_NOTID( MemberID ){ 目标 = 成员ID不为MemberID的目标; } void AI_TAR_HOJO( BuffID ){ 目标 = 有BuffIDBuff的目标; } void AI_TAR_NOTHOJO( BuffID ){ 目标 = 没有BuffIDBuff的目标; } void AI_TAR_BAD( AilmentID ){ 目标 = 陷入AilmentID异常状态的目标; } void AI_TAR_NOTBAD( AilmentID ){ 目标 = 未陷入AilmentID异常状态的目标; } void AI_TAR_WEAK_ST( ElementID ){ //AI_TAR_MUKOU_ST,AI_TAR_NOTWEAK_ST等也是合法函数名 目标 = ElementID属性为弱点的目标; }
AI_CHK函数:检查并返回数值
int AI_CHK_MORE(){ if <发生one more> then return 1; else return 0; } int AI_CHK_MYHP( int x ){ if <自身当前HP百分比 <= x%> then return 1; else return 0; } int AI_CHK_ID( MemberID EnemyID ){ if <MemberID or EnemyID存活> then return 1; else return 0; } /*int AI_CHK_ID_ENTARGET( int x ){ if < x > then return 1; else return 0; }*/ int AI_CHK_MYHOJO( BuffID ){ if <自身有BuffID的Buff> then return 1; else return 0; } int AI_CHK_NOTMYHOJO( BuffID ){ if <自身没有BuffID的Buff> then return 1; else return 0; } int AI_CHK_MYBAD( AilmentID ){ if <自身陷入AilmentID异常状态> then return 1; else return 0; } int AI_CHK_NOTMYBAD( AilmentID ){ if <自身未陷入AilmentI异常状态> then return 1; else return 0; } int AI_CHK_MYUSESKIL( SkillID ){ if <自身使用过SkillID的技能> then return 1; else return 0; } int AI_CHK_MYUSEATTR( ElementID ){ if <自身使用过ElementID的属性攻击> then return 1; else return 0; } int AI_CHK_ENHOJO( BuffID ){ if <敌人有BuffID的Buff> then return 1; else return 0; } int AI_CHK_ENID( PartyID ){ if <PartyID存活> then return 1; else return 0; } int AI_CHK_ENIDHOJO( PartyID, BuffID ){ if <PartyID有BuffID的Buff> then return 1; else return 0; } int AI_CHK_NOTENHOJO( BuffID ){ if <敌人没有BuffID的Buff> then return 1; else return 0; } int AI_CHK_ENBAD( AilmentID ){ if <有敌人陷入AilmentID异常状态> then return 1; else return 0; } int AI_CHK_ENIDBAD( PartyID, AilmentID ){ if <PartyID陷入AilmentID异常状态> then return 1; else return 0; } int AI_CHK_NOTENBAD( AilmentID ){ if <没有敌人陷入AilmentID异常状态> then return 1; else return 0; } int AI_CHK_ENWEAK( ElementID ){ if <有敌人ElementID属性为弱点> then return 1; else return 0; } int AI_CHK_ENIDWEAK( PartyID, ElementID ){ if <PartyID的ElementID属性为弱点> then return 1; else return 0; } int AI_CHK_ENHANSYA( ElementID ){ if <有敌人ElementID属性为反弹> then return 1; else return 0; } int AI_CHK_ENIDHANSYA( PartyID, ElementID ){ if <PartyID的ElementID属性为反弹> then return 1; else return 0; } int AI_CHK_ENKYUSYU( ElementID ){ if <有敌人ElementID属性为吸收> then return 1; else return 0; } int AI_CHK_ENIDKYUSYU( PartyID, ElementID ){ if <PartyID的ElementID属性为吸收> then return 1; else return 0; } int AI_CHK_ENMUKOU( ElementID ){ if <有敌人ElementID属性为无效> then return 1; else return 0; } int AI_CHK_ENIDMUKOU( PartyID, ElementID ){ if <PartyID的ElementID属性为无效> then return 1; else return 0; } int AI_CHK_ENCNT( int x ){ if <敌人数量 == x> then return 1; else return 0; } int AI_CHK_ENEMY_ABLEWEAK( EnemyID ){ if <EnemyID可以攻击敌人的弱点> then return 1; else return 0; } int AI_CHK_FRIDHP( EnemyID, int x ){ if <EnemyID单位当前HP百分比 <= x%> then return 1; else return 0; } int AI_CHK_FRBAD( AilmentID ){ if <友方陷入AilmentID异常状态> then return 1; else return 0; } int AI_CHK_FRIDBAD( EnemyID, AilmentID ){ if <EnemyID单位陷入AilmentID异常状态> then return 1; else return 0; } int AI_CHK_FRHOJO( BuffID ){ if <友方有BuffID的Buff> then return 1; else return 0; } int AI_CHK_FRIDHOJO( EnemyID, BuffID ){ if <EnemyID单位有BuffID的Buff> then return 1; else return 0; } int AI_CHK_TURN_O( int x ){ if <当前回合数 >= x> then return 1; else return 0; }
AI_GET函数:获取数值
int AI_GET_FIRST_ACTION(){ if <战斗开始时自身先行动> then return 2; else return 1; } int AI_GET_E_NUM(){ return 敌人的数量; } int AI_GET_ENHOJO_ON( BuffID ){ return 敌人BuffID的Buff的数量; } int AI_GET_ENBAD_ON( AilmentID ){ return 敌人陷入AilmentID异常状态的数量; } int AI_GET_ENID_CURRENTSERIAL( EnemyID ){ return EnemyID当前行动数; } int AI_GET_BOSSDAMAGE(){ return 受到的总伤害; } int AI_GET_GLOBAL( int x ){ return 全局变量x; } int AI_GET_LOCAL_PARAM( int x ){ return 局部参数x; } /*int AI_GET_UNIHPMIN(){ return HP最低者的ID; }*/
BIT函数
int BIT_CHK( BitID ){ if <BitID处于ON状态> then return 1; else return 0; } void BIT_ON( BitID ){ BitID ON; } void BIT_OFF( BitID ){ BitID OFF; }
其他函数
void AI_CLEAR_BOSSDAMAGE(){ 受到的总伤害 = 0; } void GET_COUNT( 308 ){ 使用人格面具; } void SET_COUNT( 308, PersonaID ){ 人格面具 = PersonaID; } int RND( int x ){ return >=0且<=x的随机数; } int REM( int x, int y ){ return x%y; } PUTS( "1more" ) //给出ONE MORE
ID枚举
enum MemberID{ JOKER = 1, SKULL = 2, MONA = 3, PANTHER = 4, FOX = 5, QUEEN = 6, NAVI = 7, NOIR = 8, CROW = 9, VIOLET = 10, }; enum ElementID{ 物理 = 0, 枪击 = 1, 火焰 = 2, 冰冻 = 3, 电击 = 4, 疾风 = 5, 核热 = 6, 念动 = 7, 祝福 = 8, 咒怨 = 9, 万能 = 10, }; enum AilmentID{ 燃烧 = 1, 冰冻 = 2, 触电 = 4, 晕眩 = 11, 混乱 = 12, 恐惧 = 13, 遗忘 = 14, 饥饿 = 15, 睡眠 = 16, 狂怒 = 17, 绝望 = 18, 洗脑 = 19, // = 0x0200, 死亡 = 0x00080000, // = 0x00100000, /* 扭曲的嫉妒 = ?, 扭曲的色欲 = ?, 扭曲的贪婪 = ?, 扭曲的虚荣 = ?, 死亡行进 = ?, 恐慌 = ?, 老鼠化 = ?, */ }; enum BuffID{ 塔尔卡加 = 1, 塔伦达 = 2, 革命 = 3, 反叛 = 5, 斯库卡加的命中率Buff = 4, 斯库卡加的回避率Buff = 0x40, 斯坤达的命中率Buff = 8, 斯坤达的回避率Buff = 0x80, 拉库卡加 = 0x10, 拉坤达 = 0x20, 蓄力 = 0x0100, 专心致志 = 0x0200, 提特拉康 = 0x4000, 玛卡拉康 = 0x8000, //提特拉加 = ?, 防火之壁 = 0x10000, 防冰之壁 = 0x20000, 防电之壁 = 0x40000, 防岚之壁 = 0x00080000, 防核之壁 = 0x00100000, 防念之壁 = 0x00200000, 污秽空气 = 0x00400000, 污秽吐息 = 0x00400000, };
修改项目名称
为了简单化操作流程,请使用已扩充字符的简体中文NAME.TBL进行编辑。
NAME.TBL的文本内容由其下的奇数号DAT文件决定。
修改主要项目的名称:以技能名称“哈米吉多顿”为例
主要项目包括:
技能
特性
敌方单位
人格面具
饰品
防具
它们对应的已扩充字符的奇数号DAT文件为:
newName(03).DAT: 技能,每项占30字节;
newName(05).DAT: 技能(不完整),每项占30字节;
newName(07).DAT: 敌方单位,每项占25字节;
newName(09).DAT: 人格面具,每项占20字节;
newName(11).DAT: 特性,每项占20字节;
newName(13).DAT: 饰品,每项占20字节;
newName(15).DAT: 防具,每项占20字节。
现在着手修改技能名称“哈米吉多顿”。
我的目的是将拉雯妲的“米吉多拉翁”(ID603)的名称改为“哈米吉多顿”。
首先调整010 editor的编辑器格式。使用010 editor打开技能名称对应的DAT文件newName(03).DAT,将上侧边栏的行宽改为31(每项的所占字节数+1 = 30 + 1),将左侧边栏的地址改为“行号(十进制)”;
此时编辑器中的行号即为技能ID。
生成汉字的十六进制编码。打开P5R汉字转编码&字库,在Sheet9的对应单元格分别输入“哈米吉多顿”;
在右侧的单元格复制对应的十六进制编码,并将其粘贴至任意窗口,然后复制其非零位编码;
注意不要截断编码!因为非零位末尾有可能出现"XX X0"这样的编码,所以你需要检查复制的位数是否为4的倍数,如果不是就再复制非零位末尾的一个0;
粘贴编码。在010 editor中找到第604行的位置,选中前20(5×4) 位的编码,依次点击“编辑”—>“粘贴自”—>“粘贴自十六进制文本”;
编码已替换,保存;
最后更新Name.TBL文件。打开PersonaEditor.exe并将Name.TBL移入左下侧框中,右键其下的Name(03).DAT并点击REPLACE,选择newName(03).DAT替换并保存,保存的Name.TBL即为修改好的文件。
至此,拉雯妲的“米吉多拉翁”(ID603)的名称已修改为“哈米吉多顿”。
另:批量生成简体中文的十六进制编码
用符号分隔原文本中的汉字;
全选,将文本转换成表格,以符号作为分隔位置(列数21 = 1列符号 + 20列汉字);
复制生成的表格,粘贴到P5R汉字转编码&字库Sheet9的对应单元格;
右侧的单元格内已生成对应的十六进制编码。
修改其他项目的名称:以材料名称“黑雪人之光”为例
对于简体中文,此过程十分繁琐!
为了能够理解该部分的操作流程的意义,需要先了解NAME.TBL的构成。
打开PersonaEditor.exe并将NAME.TBL移入左下侧框中,发现其下有38个dat格式的文件。
它们前后每两个为一组,前者储存字符长度数据,后者储存字符数据。
如NAME(02).DAT储存技能名称的字符长度数据,后面的NAME(03).DAT储存技能名称的字符数据。
以下列出所有奇数号文件所储存的内容:
NAME(01).DAT: 塔罗牌名称,
NAME(03).DAT: 技能名称,
NAME(05).DAT: 技能名称(不完整),
NAME(07).DAT: 敌方单位名称,
NAME(09).DAT: 人格面具名称,
NAME(11).DAT: 特性名称,
NAME(13).DAT: 饰品名称,
NAME(15).DAT: 防具名称,
NAME(17).DAT: 消耗道具名称,
NAME(19).DAT: 重要道具名称,
NAME(21).DAT: 材料名称,
NAME(23).DAT: 近战武器名称,
NAME(25).DAT: 战斗行动名称,
NAME(27).DAT: 服装名称,
NAME(29).DAT: 技能卡名称,
NAME(31).DAT: 人物简称,
NAME(33).DAT: 成员和协助人姓氏,
NAME(35).DAT: 成员和协助人名字,
NAME(37).DAT: 远程武器名称。
现在着手修改材料名称“黑雪人之光”。
打开Persona5NameTableEditor.exe并将Name.TBL移入窗口中,自动反编译生成17个txt格式的文件:
之所以生成17个而非19(38÷2)个文件,是因为Persona5NameTableEditor.exe完全兼容P5,但与P5R兼容性并不好。这也是为什么生成的部分文件,其文件名与内容并不相符。而且它不支持简体中文,这会导致所生成文件的内容是表面上的乱码;
找到实际内容是材料名称的文件(这里是BattleActionNames.txt)并打开,找到第2行,这是“RESERVE”对应的乱码;
每个空格、乱码或汉字对应AA,用14个A替换掉乱码并保存;
打开Persona5NameTableEditor.exe并输入包含txt文件的文件夹路径,自动编译生成newName.tbl;
打开PersonaEditor.exe并将newName.tbl移入左下侧框中,右键其下的newName(20).DAT并点击Save As...另存为;
选择newName(21).DAT,找到14个A并记录其位置;
将原Name.TBL移入左下侧框中,右键其下的Name(20).DAT并点击REPLACE,选择newName(20).DAT替换。这一步骤是为了使用正确的字符长度文件;
右键Name(21).DAT并点击Save As...另存为,使用010 Editor打开,找到它对应的14个A(这个文件里实际上没有这10个A)的位置即第6步中所记录的位置,改成“黑雪人之光”对应的编码并保存;
在PersonaEditor.exe内右键Name(21).DAT并点击REPLACE,选择刚修改的Name(21).DAT替换。
点击File—>Save As...,保存的Name.TBL即为修改好的文件。
至此,ID为2的材料的名称已修改为“黑雪人之光”。
修改文本信息:以“亚基”的技能描述为例
技能的数据存储在文件:
BASE\SC\INIT\DATMSG.PAK
使用PersonaEditor批量编译或反编译和替换BF和BMD等类型文件。
打开PersonaEditor.exe后,将DATMSG.PAK移入左下侧框中,选择其下的datSkillHelp.bmd并在右侧进行如图设置,可以看到所有技能描述。

找到skill00A“亚基”,将其描述修改为:


进入游戏测试后可见,超过3行的技能描述会超出窗口显示范围。
使用AtlusScriptCompiler单独编译或反编译BF和BMD格式的文件。
对于不包含简体中文的文件(如AI文件):打开原版AtlusScriptCompilerGUI.exe后,将BF或BMD文件移入左侧框中,自动反编译生成对应flow或msg文件(均可使用记事本直接编辑);保存后移入右侧框中,自动编译生成BF或BMD文件;重命名新生成的文件名使其与原名一致。
对于包含简体中文的文件(如技能描述):请使用简中版AtlusScriptCompilerGUI_DARK.exe.
至此,“亚基”的技能描述已变得更加详细。
修改音乐、模型……
相信你已经有了足够的信息搜集能力和动手能力,请尝试自己修改吧!
测试模组
需要在Reloaded-II中创建一个新的模组。
打开Reloaded-II, 点击窗口左侧第二个图标(Manage Mods).
点击New创建模组。
以下操作请自己命名,不要照搬图例!
模组Id, 这是你的模组文件夹名:

常规细节:

必需依赖模组,选择Persona Essentials:

更新支持,根据自己需要选择:

选择可使用该模组的游戏Persona 5 Royal:

此时,已经可以在P5R的模组列表中看到新创建的P5R MOD TEST.

右键该模组并点击Open Folder, 发现文件夹中存在ModConfig.json, 这是自动生成的模组设置文件。
新建若干个文件夹,确保其结构如下:
P5RPC.ModTest ├─P5REssentials │ └─CPK │ ├─BASE.CPK │ | ├─battle │ | │ ├─SCRIPT │ | │ ├─SKILL │ | │ └─TABLE │ | ├─sound │ | | └─SKILLSE │ | └─MODEL │ | └─CHARACTER | └─SC | ├─battle │ │ └─TABLE | └─INIT | └─ModConfig.json
将你修改的文件复制进对应文件夹。
点击Launch Application启动游戏来调试你的模组。
发布模组
打开Reloaded-II, 点击窗口左侧第二个图标(Manage Mods).
选中模组并点击Publish. 根据自己的需要编辑发布设置:

点击Pulish后,你可以在输出文件夹中看到模组压缩包。