188 lines
6.8 KiB
Markdown
188 lines
6.8 KiB
Markdown
# 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/SWF**。这是"为什么必须操作 edit.xml"和"为什么钩子模式有价值"的根本原因。
|
||
|
||
---
|
||
|
||
## 2. 开始前
|
||
|
||
### 必读
|
||
|
||
**APT修改指南.md** — CDATA 格式、指令句法、ru 公式、placeobject flags —— **必须先读**。
|
||
|
||
### 分析流程
|
||
|
||
修改任何 APT 前,按以下顺序分析:
|
||
|
||
1. **读反编译 AS** → 理解类结构、方法、逻辑流
|
||
- `Data/decompiled-as-references/<apt名>/scripts/__Packages/<类名>.as`
|
||
- 共享类在 `Data/decompiled-as-references/main_mouse/scripts/`(如 `std_mouseRadioButton`、`mouse_chapterSelectComponent`)
|
||
2. **读 edit.xml** → 布局、角色(image/shape/sprite/button)、placeobject、bytecode
|
||
3. **跨文件追踪** → `export`/`import` 关系:一个 APT 导出的 character 可被其他 APT 引用;
|
||
运行时 `attachMovie()` 按符号名解析——只要有一条 import 链可达即可
|
||
4. **请求原始参考 APT** → 推断 tx 语义、对齐关系必须对比未修改版;
|
||
请求用户提供原始 APT(如本项目中的 `Data/aptui_ref/`)
|
||
5. **从原始数据推算** → 不假设 tx 是左边缘或中心,用 shape 尺寸交叉验证;
|
||
按钮与背景图对齐关系需从原始坐标推算
|
||
|
||
---
|
||
|
||
## 3. 工具
|
||
|
||
编辑后需要用 `ra3apt-cli` 编译:
|
||
|
||
```sh
|
||
# 反编译 .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` 变量传递。
|
||
|
||
**具体步骤**:
|
||
|
||
1. 在可编辑 screen 的 `initGUI()` 末尾插入以下 bytecode(位置:`registerIntroOuttroComponents()` / `startIntro()` 调用之后、`end` 之前):
|
||
|
||
```as
|
||
// 等效 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:
|
||
```
|
||
|
||
2. 在新 FLA 的 ActionScript 中编写处理函数,赋值给 `_global.gHookName`
|
||
|
||
3. 钩子函数接收的 `this` 是**管理类实例**(`Cafe_BaseUIScreen` 子类),不是 MovieClip
|
||
|
||
> 初始化顺序:`frameDelayedInit() → initGUI() → registerIntroOuttroComponents() → startIntro()`
|
||
|
||
若有需要可以为 `<placeobject />` 添加 `<poname name="xxx" />` 从而让 ActionScript 能够访问对应 MovieClip 并进行操作(例如`parent.xxx._y = 100`),但不作为默认要求。
|
||
|
||
---
|
||
|
||
## 6. 输出给用户
|
||
|
||
- 需用户配合的内容(引擎常量、本地化 key、外部 AS 代码)→ 输出为独立的 `.md` 指南文件
|
||
- 指南给出可直接使用的代码/值,而非笼统描述
|