Files
AptXmlAI/AGENTS.md
T
2026-06-14 01:40:04 +02:00

188 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 指南文件
- 指南给出可直接使用的代码/值,而非笼统描述