6.8 KiB
AGENTS.md
本项目工作:修改 RA3 UI(APT 文件)。
1. 概述
APT 是 RA3 的 Flash-like UI 二进制格式。修改 APT 由两类工作组成:
- 修改布局和素材:调整位置、大小、纹理、角色(shape/sprite/button)
- 修改脚本/逻辑:改写 bytecode(汇编),或通过钩子引入外部 ActionScript
edit.xml 是什么
_edit.xml 是 .apt 二进制反编译后的可编辑 XML 版本。
修改 _edit.xml 后需用工具编译回 .apt。
两种生成 APT 的路径
| 路径 | 适用 | 能写 AS? |
|---|---|---|
| 已有 APT → edit.xml → 修改 → 编译回 APT | 修改现有 UI | 只能写汇编(bytecode) |
| 新 FLA → SWF → APT | 创建全新 UI | 可在 Flash 中直接写 ActionScript |
已有 APT 无法逆向转回 FLA。这是"为什么必须操作 edit.xml"和"为什么钩子模式有价值"的根本原因。
2. 开始前
必读
APT修改指南.md — CDATA 格式、指令句法、ru 公式、placeobject flags —— 必须先读。
分析流程
修改任何 APT 前,按以下顺序分析:
- 读反编译 AS → 理解类结构、方法、逻辑流
Data/decompiled-as-references/<apt名>/scripts/__Packages/<类名>.as- 共享类在
Data/decompiled-as-references/main_mouse/scripts/(如std_mouseRadioButton、mouse_chapterSelectComponent)
- 读 edit.xml → 布局、角色(image/shape/sprite/button)、placeobject、bytecode
- 跨文件追踪 →
export/import关系:一个 APT 导出的 character 可被其他 APT 引用; 运行时attachMovie()按符号名解析——只要有一条 import 链可达即可 - 请求原始参考 APT → 推断 tx 语义、对齐关系必须对比未修改版;
请求用户提供原始 APT(如本项目中的
Data/aptui_ref/) - 从原始数据推算 → 不假设 tx 是左边缘或中心,用 shape 尺寸交叉验证; 按钮与背景图对齐关系需从原始坐标推算
3. 工具
编辑后需要用 ra3apt-cli 编译:
# 反编译 .apt → edit.xml
ra3apt-cli expand <input.apt>
# 编译 edit.xml → .apt
ra3apt-cli build <input_edit.xml>
工具有可能不在当前工作区;如果可执行,用以上命令转换(生成的文件会替换已有文件);如果不可用,提醒用户手动操作
格式规则
- 所有 bytecode(
<action>、<clipaction>、<initaction>)使用<![CDATA[...]]>包裹 - CDATA 内
&等字符无需 XML 实体转义(写&Outline,非&Outline) - 修改后用 grep 验证关键值(坐标、depth、label)
4. 修改布局和素材
depth 规则
placeobject的depth控制 Z 序(渲染和点击优先级)- 新增元素前必须 grep 验证该 depth 未被同一帧的其他 placeobject 占用
单选按钮(std_mouseRadioButton)
m_width不控制视觉宽度(只用于labelTF._x文本居中)- 缩放用
rotm00(宽)和rotm11(高)。缩放后ty需补偿垂直偏移:new_ty = old_ty + old_visual_height × (1 − scale) / 2 - mouse_chapterSelectComponent 的特殊情况(不一定适用于其他 APT 文件):按钮与背景图是独立层,对齐偏移需从原始坐标推算
纹理 ru
tex_x = m00 × screen_x + tx
tex_y = m11 × screen_y + ty
t命令中坐标为屏幕坐标- 矩阵为单位矩阵(
1:0:0:1)时简化为tex = screen + tx - 移动屏幕三角形、保持采样同一纹理区域:
new_tx = tex_left − new_screen_left - 缩小屏幕三角形后要完整显示纹理(非裁切):
m00 = tex_width / new_width,再重算tx - 多个 shape 可从同一张
.tga图集的不同坐标区域采样(texture atlas 模型)
Content Sprite 结构
- 使用帧标签定义状态:
_disable/_selected/_unselected/_selectedRollover/_unselectedRollover/_downState/_intro/_outtro - 须包含
_parent.introComplete()和_parent.outtroComplete()回调 - 新增 sprite 时必须从同一 APT 现有 content sprite 复制完整帧结构(帧数可能不同)
5. 修改脚本/逻辑
_global 通信总线
- 所有 APT 和外部 AS 共享同一个
_global命名空间 - 一个 APT 中定义的全局变量(如
_global.ENUM.XXX)可在其他 APT 的 bytecode 中读取 - 这是钩子模式能成立的基础
this 的含义
Cafe_BaseUIScreen子类的 initaction 中,this是管理类实例,不是 MovieClip- MovieClip 通过
this.m_screen或Cafe2_BaseUIScreen.m_screen访问 - 写 bytecode 或钩子函数都需区分两者
汇编调用与寄存器
Cafe_BaseUIScreen 子类方法中:reg1 = this,reg2 = _global。
| 操作 | 格式 |
|---|---|
| 调函数 | ...args → push nArgs → funcObj → pushundefined → callmethodpop |
| 调方法 | ...args → push nArgs → obj → callnamedmethodpop "methodName" |
- 禁止
callnamedpop "call"— 等效func["call"](),这在 APT 中不被支持 - 新增 label 名用 grep 验证不与已有冲突
汇编 vs 钩子:决策
| 情况 | 做法 |
|---|---|
| 改动少且机械(复制现有模式) | 直接写汇编在 edit.xml 中 |
| 改动复杂(多分支、循环) | 用钩子 |
目标类在不可编辑的 APT 中(无 _edit.xml) |
只能用钩子 |
钩子详解
原理:_edit.xml 中写少量检测 bytecode,实际逻辑写在新 FLA→SWF→APT 的 ActionScript 中,
通过 _global 变量传递。
具体步骤:
- 在可编辑 screen 的
initGUI()末尾插入以下 bytecode(位置:registerIntroOuttroComponents()/startIntro()调用之后、end之前):
// 等效 AS:if (_global.gHookName != undefined) { _global.gHookName(this); }
pushglobal
getnamedmember "gHookName"
pushundefined
ActionNewEquals // != undefined ?
ActionLogicalNot
ActionLogicalNot
jumptrue lbl_skip
pushreg 1 // this(管理类实例)
EAPushOne // 1 个参数
pushglobal
getnamedmember "gHookName"
pushundefined // 直接调用,非方法查找
callmethodpop
lbl_skip:
-
在新 FLA 的 ActionScript 中编写处理函数,赋值给
_global.gHookName -
钩子函数接收的
this是管理类实例(Cafe_BaseUIScreen子类),不是 MovieClip
初始化顺序:
frameDelayedInit() → initGUI() → registerIntroOuttroComponents() → startIntro()
若有需要可以为 <placeobject /> 添加 <poname name="xxx" /> 从而让 ActionScript 能够访问对应 MovieClip 并进行操作(例如parent.xxx._y = 100),但不作为默认要求。
6. 输出给用户
- 需用户配合的内容(引擎常量、本地化 key、外部 AS 代码)→ 输出为独立的
.md指南文件 - 指南给出可直接使用的代码/值,而非笼统描述