first commit

This commit is contained in:
2026-06-14 01:40:04 +02:00
commit 5511354783
4787 changed files with 103543 additions and 0 deletions
+187
View File
@@ -0,0 +1,187 @@
# 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`,非 `&amp;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
// 等效 ASif (_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` 指南文件
- 指南给出可直接使用的代码/值,而非笼统描述