Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
863a134
feat: 式舆防卫战自动配队
kawayiYokami Jul 30, 2026
69b77be
feat(devtools): 添加配队选择
kawayiYokami Jul 30, 2026
bef1d0b
fix: 让按住滑动更加稳定
kawayiYokami Jul 30, 2026
f656e39
fix: 使用拖动翻页预备编队
kawayiYokami Jul 30, 2026
6000420
chore: 增强预备编队扫描日志
kawayiYokami Jul 30, 2026
02244b6
fix: 跳过被禁用的预备编队
kawayiYokami Jul 30, 2026
083fe9d
fix: 取消预选的预备编队
kawayiYokami Jul 30, 2026
039948c
docs: 补充预备编队选择说明
kawayiYokami Jul 30, 2026
a0d2b23
fix: 限制预备编队选中确认重试
kawayiYokami Jul 30, 2026
fc75a0f
fix: 限制扫描预备编队代理人数
kawayiYokami Jul 30, 2026
682f4fb
docs: 说明预备编队邦布处理
kawayiYokami Jul 30, 2026
a72b307
Merge remote-tracking branch 'origin/main' into feat/shiyu-defense-au…
DoctorReid Jul 31, 2026
49b1f9d
docs(combat): 补战斗系统机制与配队评分注释
DoctorReid Jul 31, 2026
7bd1652
fix(shiyu_defense): 选队改用核心技X/3判满员并容错OCR误读
DoctorReid Aug 1, 2026
b81ab37
fix: 统一预备编队名称空白
kawayiYokami Aug 1, 2026
e1f60bd
fix: 按预备编队序号定位选卡
kawayiYokami Aug 1, 2026
437fd6a
docs: 说明预备编队名称同步与序号选卡
kawayiYokami Aug 1, 2026
bd68007
fix: 延长前台拖动按住时间
kawayiYokami Aug 1, 2026
bf86701
feat: 新增预备编队坐标区域
kawayiYokami Aug 1, 2026
c6bc4c8
fix: 改进预备编队禁用识别
kawayiYokami Aug 1, 2026
39cb37c
fix: 修正预备编队禁用与选队定位
kawayiYokami Aug 1, 2026
fb2cd68
feat: 配队选择改为独立插件
kawayiYokami Aug 1, 2026
55bbe70
chore: 优化预备编队选择日志
kawayiYokami Aug 1, 2026
d02acc9
fix: 拖动前停顿改为类常量并计入拖动时长
kawayiYokami Aug 3, 2026
c96a41c
chore: 删除配队选择测试应用
kawayiYokami Aug 3, 2026
97305fd
fix: 预备编队槽位改为从0开始计数
kawayiYokami Aug 7, 2026
81eedc5
chore: 清理已删除配队选择应用的 STANDALONE 残留
kawayiYokami Aug 12, 2026
bef3370
style
ShadowLemoon Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 87 additions & 8 deletions docs/game/gameplay/combat.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,94 @@
---
gameplay_name: 战斗系统
involves_screens: []
involves_screens: [战斗画面]
last_updated: 2026-07-31
---

# 战斗系统
# 战斗系统(玩法)

## 机制
(自由描述:战斗核心机制、角色切换、属性克制等。)
绝区零的核心战斗机制。本页只讲**通用共性机制**(所有战斗玩法共用),作为理解自动配队 / 自动战斗的依据。个别角色的专属机制(如某某角色的独有架势 / 染色 / 能量条)不展开——那是角色百科内容,查 [米游社 WIKI](https://baike.mihoyo.com/zzz/wiki/channel/map/66/68) 或 [萌娘百科](https://zh.moegirl.org.cn);只在**代码用到时**在本页点到。

## 流程与决策点
(自由描述:进入战斗 → 编队 → 开打 → 结算 的画面流转;关键决策点。)
战斗画面识别见 [战斗画面.md](../screens/战斗画面.md)。

## 备注
(自由记录。)
## 这是什么

战斗是绝区零绝大多数玩法(式舆防卫战 / 恶名狩猎 / 迷失之地 / 各类副本)的核心:3 人小队实时切人打怪,靠 **属性克制 + 异常积蓄 + 失衡击破** 三条机制链路输出伤害。配队围绕这三条链路 + 角色职业搭配展开。

## 三条输出链路

### 1. 属性克制(直伤队核心)

- **7 种属性**:电、以太、物理、火、冰、风(3.0 引入)、流明(对应代码 `DmgTypeEnum`)。
- **克制(弱点)**:敌人对该属性抗性 **-20%**,直伤更高(伤害数字白色)。
- **抗性**:敌人对该属性抗性 **+20%**,直伤更低(伤害数字变灰)。
- 非克制非抗性:抗性 0,正常伤害。

> **配队依据**:尽量用敌人**弱点属性**的输出角色,避开**抗性属性**。脚本据此给候选队评分(见末尾「自动配队评分」)。

来源:[NGA · 绝区零数据机制导论](https://bbs.nga.cn/read.php?tid=40893992)。

### 2. 异常积蓄 + 紊乱(异常队核心)

- **异常积蓄**:用同属性攻击持续打敌人,积累「异常值」(怪物血条右侧进度条);攒满触发该属性的**异常效果**(各属性效果不同,分持续伤害类 / 单次伤害类 / 控制类,具体查 WIKI)。
- **紊乱(Disorder)**:对已处于某异常状态的敌人,再施加**另一种属性**的异常 → 覆盖原状态,触发**紊乱**,额外结算一次伤害。
- 异常队靠快速挂多种异常反复触发紊乱打伤害(混伤,以异常为主、直伤为辅)。
- 异常伤害受「异常精通 + 攻击力 + 异常掌控」影响,**不受暴击影响**(培养方向与直伤角不同)。

> 注意游戏内正式术语是「**紊乱**」(Disorder);风属性版的紊乱叫「**乱流**」(风专属别名,见下「染色」),"紊流"则是误写。

来源:[游民星空 · 全属性异常效果一览](https://www.gamersky.com/handbook/202407/1784391.shtml)、[TapTap · 紊乱机制解读](https://www.taptap.cn/moment/591659398095440330)。

### 3. 失衡 + 连携技(击破队核心)

- 攻击积累敌人**失衡值**(血条下方进度),满后敌人进入「失衡状态」,期间受到的伤害大幅提升。
- 失衡期间触发**连携技**(角色大招演出),是爆发输出窗口。
- **击破**职业负责快速打失衡,把队伍"抬"进连携爆发期。

## 染色(风 / 流明异常的特殊机制)

风、流明是较新的特殊异常属性,有专属异常交互(代表角色:风=维琳娜、流明=蕾米埃尔,角色技能详见 WIKI),是当前版本异常队核心。

- **风属性**:异常体系为**风化**(持续伤害)→ **浸染 / 染色**(接触其他属性时提升风 + 该属性伤害)→ **乱流**(风化与其他属性异常共存时触发爆炸,风版紊乱)。乱流靠**队友的异色异常**触发。
- **流明属性**:**可变属性**——流明异常会随队友属性变化(与风属性队友 → 算风伤,与物理 → 物伤,与以太 → 以太伤)。

**风 + 流明同队**:流明因风属性队友而变风伤 → 双方都打风,**没有异色异常可触发风乱流**。配队评分据此对两者做不对称处理:

- 风异常的乱流染色源**不能是流明队友**(流明会变风,非异色)→ 评分时 `WIND` 排除 `LUMIFLUX` 队友。
- 流明可变,能吃任何队友属性(含风)→ 评分时 `LUMIFLUX` 不排除队友。

见 `shiyu_defense_team_utils.py:_calc_team_score` 染色分支。

来源:[游侠 · 维琳娜技能介绍](https://m.ali213.net/news/gl2606/1783305.html)、[4399 · 蕾米埃尔全面解析](https://www.4399.com/pcgame/topic/53681368.html)、[NGA · 蕾米埃尔绑定维琳娜讨论](https://bbs.nga.cn/read.php?tid=47024817)。

## 职业系统

每个代理人有**职业**(`AgentTypeEnum`),决定战斗定位:

| 职业 | 定位 | 配队评分角色 |
|---|---|---|
| 强攻(ATTACK) | 直伤输出主力 | 输出(按属性克制评分) |
| 命破(RUPTURE) | 直伤输出(另一套体系) | 输出 |
| 异常(ANOMALY) | 靠异常积蓄 / 紊乱打伤害 | 输出(异常倍率;风/流明走染色分支) |
| 击破(STUN) | 打失衡触发连携 | 辅助(对齐最强输出计分) |
| 支援(SUPPORT) | 增伤 / 治疗 / buff | 辅助 |
| 防护(DEFENSE) | 护盾 / 生存 | 辅助 |

## 虚狩

**虚狩**是新艾利都调查员的**最高荣誉称号**(由市长授予,表彰镇压空洞的极大贡献),**不是职业也不是属性**,而是剧情设定上的头衔。当前虚狩级代理人(对应代码 `VOID_HUNTER_AGENT_ID_LIST`):仪玄(`yixuan`)、星见雅(`hoshimi_miyabi`)、叶瞬光(`yeshunguang`)、蕾米埃尔(`remielle`)。

> 代码里 `VOID_HUNTER_*` 命名是"虚狩"的直译(早期被误称为"虚无猎人")。虚狩角色通常机制 / 数值更强,脚本配队给虚狩角色 **1.2 倍评分加权**。该列表**硬编码**,游戏新增虚狩角色时需手动补(后续可考虑下沉到 `Agent` 数据字段派生)。

来源:[萌娘百科 · 虚狩](https://zh.moegirl.org.cn/zh-cn/%E8%99%9A%E7%8B%A9)。

## 关联:自动配队评分(代码)

式舆防卫战自动配队(`shiyu_defense_team_utils.py:DefenseTeamSearcher` / `select_teams`)用上述机制给候选预备编队打分,选**总分最高且两阶段代理人不重复**的组合:

- **输出角色(强攻 / 命破 / 异常)**:按属性对弱点 / 抗性的契合度评分。代码用 `1.3 / 0.7`(异常 `1.69 / 0.49`)——**这是评分权重,不是真实 ±20% 伤害倍率**;放大克制 / 抗性的分差以便排序。
- **辅助角色(击破 / 支援 / 防护)**:按「对齐队内最强输出」计入,每个辅助贡献 = 最强输出分(`sum(output) + max(output) × 辅助数`)。
- **虚狩角色**:1.2 倍加权。
- **风 / 流明异常**:走染色分支(看队友异常属性)。
- 普通节点 2 个半场 / 多间模式全部未完成房间的队,代理人 ID 不能重复。

配队的完整玩法流程见 [式舆防卫战.md](式舆防卫战.md);扫描 / 点击选队的自动化实现见相关 application / operation 代码。
6 changes: 3 additions & 3 deletions docs/game/gameplay/式舆防卫战.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ involves_screens: [式舆防卫战, 式舆防卫战-三间选择, 快捷手册,
- **推荐属性(弱点)**:用这些属性的代理人伤害更高。
- **敌人抗性**:这些属性的代理人伤害被削,避免用。

据此为每个半场配克制队 —— 这是「配队策略」核心。脚本 `calc_teams` 就是按弱点匹配预备编队(见附录)
据此为每个半场配克制队。脚本在式舆防卫战的预备编队选择界面扫描游戏内编队并写入预备编队配置,再以角色职业、属性和本场弱点/抗性计算总分;不用手工配置队伍应对的属性

### 防线效果(Frontier Effect)
每期剧变节点有**整期共享的特殊增益**(如「冰属性伤害+10%、异常精通+40」),利用效果配队更易通关。2.5 改版后所有防线共享同一效果(来源:Game8)。
Expand Down Expand Up @@ -88,9 +88,9 @@ involves_screens: [式舆防卫战, 式舆防卫战-三间选择, 快捷手册,
⚠️ **只自动化剧变节点**(稳定/纷争是永久一次性,玩家手动通一次即可,不值得写脚本)。

**普通防线(1-4)节点链**:
传送(TransportByCompendium 作战/式舆防卫战/剧变节点)→ 等加载(关"前次行动最佳记录"弹窗 + 等"前哨档案")→ 选择节点(OCR 剧变节点进度 X/Y 首尾数字法判断从哪开始,#2510)→ 识别弱点算配队(`calc_teams`,2 半场弱点+抗性 → DFS 选预备编队,避代理人冲突)→ 选配队(`ChoosePredefinedTeam` 点编队名+200px)→ 出战(Deploy)→ 自动战斗(`ShiyuDefenseBattle`,每半场一次,op 内部移动+战斗+阶段切换)→ 下一节点(战斗结束-下一防线/退出)→ 所有节点完成 → 领奖(奖励入口→全部领取)→ 关闭奖励 → 返回大世界。
传送(TransportByCompendium 作战/式舆防卫战/剧变节点)→ 等加载(关"前次行动最佳记录"弹窗 + 等"前哨档案")→ 选择节点(OCR 剧变节点进度 X/Y 首尾数字法判断从哪开始,#2510)→ 识别弱点→进入预备编队选择(扫描游戏列表并按顺序写入名称、代理人到当前实例预备编队配置,首个空队结束)→自动配队(`select_teams` 为两个半场选总分最高、编队和代理人 ID 都不重复的队伍)→选配队(`ChoosePredefinedTeam` 按 `team_idx` 翻到目标页,以 OCR 找到目标编队名称后点击其右侧空白区,并确认 `SELECT` 已变为选中态)→预备出战→出战(Deploy)→自动战斗(`ShiyuDefenseBattle`,每半场一次,op 内部移动+战斗+阶段切换)→下一节点(战斗结束-下一防线/退出)→所有节点完成 → 领奖(奖励入口→全部领取)→关闭奖励 → 返回大世界。

**防线 5(多间模式)**:选节点-05 → 三间选择(`calc_teams_for_multi_room` 3 房配队)→ 循环{前往第N间 → 实战模拟室预备编队 → 出战 → `ShiyuDefenseBattle` → 战斗结束-退出}→ 返回主界面 → 领奖。
**防线 5(多间模式)**:选节点-05 → 三间选择(识别所有未完成房间的弱点、抗性并保留选择目标)→第一次进入房间时,在预备编队列表扫描并一次选出所有互斥队伍→循环{每间只按已选的 `team_idx` 点当前队 → 出战 → `ShiyuDefenseBattle` → 战斗结束-退出}→返回主界面 → 领奖。

**关键 op(`ShiyuDefenseBattle`)内部**:等战斗画面 → 检测防卫战倒计时(pipeline)→ 有倒计时=战斗中跑 auto_battle / 无倒计时按距离移动(move_w)→ 一层敌人清完(连续5秒无倒计时)→ `move_after_battle`:检测距离/传送点(防卫战空洞传送点 pipeline)/按键-交互 → 自移动 + F 交互进下一层 → 直到该半场胜利。**op 完整封装移动+战斗+阶段切换,不需外部干预**(详见 [画面 doc](../screens/式舆防卫战.md)「选配队与触发战斗」)。

Expand Down
16 changes: 8 additions & 8 deletions docs/game/screens/式舆防卫战.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ source_image: screens/式舆防卫战/选关主界面-3of5.webp
|---|---|---|
| 选关主界面 | 传送进 / 战斗结束返回 | 点节点-XX → 弱点 |
| 弱点+抗性+选配队 | 选关后 | 点角色头像 → 选配队 |
| 预备编队管理+选队 | 点角色头像 | 选队(+200px)+预备出战出战 |
| 预备编队管理+选队 | 点角色头像 | 扫描列表按目标评分选队 → OCR 定位队名并点右侧空白区 → 确认选中态 → 预备出战 |
| 战斗(含阶段内移动) | 出战(Deploy) | `ShiyuDefenseBattle` op 跑完该阶段 → 下一阶段 / 结算 |
| 战斗后移动(阶段内层切换) | 一层敌人清完 | op 自移动+F 交互 → 下一层 / 该阶段胜利 |
| 挑战结果(结算) | 防线/阶段胜利 | 战斗数据/退出 → 下一防线或选关 |
Expand Down Expand Up @@ -61,12 +61,12 @@ source_image: screens/式舆防卫战/选关主界面-3of5.webp

## 选配队与触发战斗(本次实操,2026-07-19)

**选配队**(app `ChoosePredefinedTeam` + `calc_teams`):
1. 选关后进弱点/编队界面(关卡特性 / 推荐属性=弱点 / 强敌抗性 / 挑战目标 / 2 阶段 TEAM SELECT)。
2. 「预备编队」按钮(413,1012)→ 预备编队管理列表(显示 `team_config` 全部编队 + 角色 + 各自 weakness)
3. ⚠️ **选队 = 点编队名右侧 +200px**(`ocr_result.max.center + Point(200, 0)`,见 `choose_predefined_team.py:82`)—— 点名称本身是**改名弹窗**,只有 +200px 才是选中。这是关键坑
4. `calc_teams` 配置驱动:OCR 弱点 area → 匹配预备编队 `weakness_list`(`ShiyuDefenseConfig`)→ DFS 选最优(弱点+1 / 抗性-1 / 避代理人头像冲突 `is_team_conflict`)。本次阶段1 风·物理抗火 → team 1 简薇耀(PHYSICAL);阶段2 火·物理抗冰 → team 4 火莱凯露(FIRE);避开 team 6 简赛露(与 team 4 共 `caesar_king` 冲突)
5. 选 2 阶段(01/02 TEAM 标记)+「预备出战」→ 回配队选择(显示选的编队)+「出战」。
**选配队**(app `ChoosePredefinedTeam` + `get_team_targets` + `select_teams`):
1. 选关后进弱点/编队界面(关卡特性 / 推荐属性=弱点 / 强敌抗性 / 挑战目标 / 2 阶段 TEAM SELECT),先识别两个半场的弱点、抗性
2. 点角色头像→「预备编队」按钮(413,1012)→预备编队管理列表。
3. `ChoosePredefinedTeam` 在当前列表内按游戏顺序扫描卡片,名称只用于抵消滚动重叠;扫描结果按首次顺序写入当前实例 `team.yml` 的同序号预备编队名称和前三名代理人,邦布不写入 `agent_id_list`,并保留该序号已有的自动战斗配置。单人、双人队合法。`1P`、`2P`、`3P` 任一标记缺失表示本期禁用:保留游戏列表序号但跳过评分;只有识别到队名且三个代理人头像均未识别时,才视为首个空队并停止扫描
4. 扫描后,`select_teams` 只使用本次写入配置的编队,按异常、强攻、命破的属性倍率计算总分;击破、支援、防护对齐队内最高倍率。普通节点的两个半场、三间模式的全部未完成房间,队伍和代理人 ID 都不能重复
5. `ChoosePredefinedTeam` 用 `team_idx` 决定滚动方向;到达目标页后 OCR 精确定位编队名称,点击名称右侧空白区。每次点击都确认同一按钮区域的 `SELECT` 已消失,并出现 `TEAM`、编号或 `SELECTED` 选中态;全部目标确认后才点「预备出战」,返回配队选择页面,再点「出战」。

**触发战斗**(app `deploy` → `shiyu_battle` → `ShiyuDefenseBattle` op):
1. 点「出战」→ 战斗画面(角色离敌人**有距离**,HP 满,未触发战斗)。
Expand Down Expand Up @@ -128,7 +128,7 @@ HP / 敌人名(UPROAR)/ 攻击按钮模板(战斗态)。进战斗时角色离敌
- 本期最佳总分(122-322,218-268,`id_mark`):画面标识。
- 重置全部(1248-1495,1009-1045):重置已打房间。
- 返回(62-173,15-94,template `menu/back`):回主界面。
- app `multi_room_*` 节点链:`calc_teams_for_multi_room`(3 房配队)→ 循环{前往第N间 → 实战模拟室预备编队 → 出战 → `ShiyuDefenseBattle` → 战斗结束-退出}→ 返回主界面 → 领奖
- app `multi_room_*` 节点链:三间选择画面一次识别全部未完成房间的弱点、抗性;进入第一间的预备编队列表时扫描并写入预备编队配置、一次选出全部互斥队伍。每间房仍只按当前房间的 `team_idx` 点选一支,后两间复用保留结果
- 玩法(3 房独立配队 / W-Engine·驱动盘锁定不共享 / 满分 50000)见 [gameplay](../gameplay/式舆防卫战.md)「防线 5 = 多间模式」。

## 备注
Expand Down
1 change: 1 addition & 0 deletions src/one_dragon/base/controller/pc_controller_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ def drag_mouse(start: Point, end: Point, duration: float = 0.5):
# 移动到起点并按下
pyautogui.moveTo(start.x, start.y)
pyautogui.mouseDown()
time.sleep(0.1)

# 减去 PcControllerBase.SLEEP_BEFORE_DRAG_END 之后的间隔
duration_drag = max(duration - PcControllerBase.SLEEP_BEFORE_DRAG_END, PcControllerBase.DRAG_MIN_DURATION)
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from one_dragon.base.operation.application import application_const
from one_dragon.base.operation.operation_node import operation_node
from one_dragon.base.operation.operation_round_result import OperationRoundResult
from zzz_od.application.devtools.shiyu_defense_team_test import (
shiyu_defense_team_test_const,
)
from zzz_od.application.devtools.shiyu_defense_team_test.shiyu_defense_team_test_config import (
ShiyuDefenseTeamTestConfig,
)
from zzz_od.application.zzz_application import ZApplication
from zzz_od.context.zzz_context import ZContext
from zzz_od.operation.choose_predefined_team import ChoosePredefinedTeam


class ShiyuDefenseTeamTestApp(ZApplication):

def __init__(self, ctx: ZContext):
ZApplication.__init__(
self,
ctx=ctx,
app_id=shiyu_defense_team_test_const.APP_ID,
op_name=shiyu_defense_team_test_const.APP_NAME,
need_check_game_win=False,
)
self.config: ShiyuDefenseTeamTestConfig = self.ctx.run_context.get_config(
app_id=shiyu_defense_team_test_const.APP_ID,
instance_idx=self.ctx.current_instance_idx,
group_id=application_const.DEFAULT_GROUP_ID,
)

@operation_node(name='扫描并选择预备编队', is_start_node=True)
def choose_team(self) -> OperationRoundResult:
op = ChoosePredefinedTeam(
self.ctx,
shiyu_target_list=self.config.get_target_list(),
start_at_team_list=True,
finish_without_confirm=False,
)
return self.round_by_op_result(op.execute())
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from one_dragon.base.operation.application.application_config import ApplicationConfig
from one_dragon.base.operation.application.application_factory import ApplicationFactory
from one_dragon.base.operation.application_base import Application
from zzz_od.application.devtools.shiyu_defense_team_test import (
shiyu_defense_team_test_const,
)
from zzz_od.application.devtools.shiyu_defense_team_test.shiyu_defense_team_test_app import (
ShiyuDefenseTeamTestApp,
)
from zzz_od.application.devtools.shiyu_defense_team_test.shiyu_defense_team_test_config import (
ShiyuDefenseTeamTestConfig,
)

if TYPE_CHECKING:
from zzz_od.context.zzz_context import ZContext


class ShiyuDefenseTeamTestAppFactory(ApplicationFactory):

def __init__(self, ctx: ZContext):
ApplicationFactory.__init__(self, shiyu_defense_team_test_const)
self.ctx: ZContext = ctx

def create_application(self, instance_idx: int, group_id: str) -> Application:
return ShiyuDefenseTeamTestApp(self.ctx)

def create_config(
self,
instance_idx: int,
group_id: str,
) -> ApplicationConfig:
return ShiyuDefenseTeamTestConfig(
instance_idx=instance_idx,
group_id=group_id,
)
Loading
Loading