Compare commits
48 Commits
4eba1c8ad8
...
new2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1c37eb510 | ||
|
|
2d11246838 | ||
|
|
d759689269 | ||
|
|
e78f4365e7 | ||
|
|
34a62f0a87 | ||
|
|
1988ae5ef1 | ||
|
|
df0dedba75 | ||
|
|
4fee02d82e | ||
|
|
15d14f7d59 | ||
|
|
9579c4f75e | ||
|
|
d54e6ce1d6 | ||
|
|
d06c99e2f1 | ||
|
|
76082d8e1f | ||
|
|
695a9e2b66 | ||
|
|
c6b5e1431f | ||
|
|
7fa18329d6 | ||
|
|
a2c397120c | ||
|
|
cb2a72da52 | ||
|
|
9da3e2d9fd | ||
|
|
eb4b370533 | ||
|
|
a3375ec3b6 | ||
|
|
011e7b3dde | ||
|
|
038abdc5d2 | ||
|
|
c27c81cfa6 | ||
|
|
a905db8e48 | ||
|
|
3060ee7df7 | ||
|
|
ab6a3b7824 | ||
|
|
a5dc1ef540 | ||
|
|
db98b60737 | ||
|
|
ccce8a11ea | ||
|
|
762dc91a11 | ||
|
|
4594f673b4 | ||
|
|
426dc24899 | ||
|
|
1609d3971a | ||
|
|
ae6c69e4f3 | ||
|
|
450a8b81e5 | ||
|
|
e328892679 | ||
|
|
486c223b65 | ||
|
|
c65c8a36ab | ||
|
|
f251e57c3c | ||
|
|
3c9496d753 | ||
|
|
409113e269 | ||
|
|
d57f4c14e0 | ||
|
|
b13178166a | ||
|
|
d692251467 | ||
|
|
805a3928a7 | ||
|
|
2507940cfd | ||
|
|
de763efb0f |
@@ -1,49 +0,0 @@
|
||||
# 技能配置重构实施计划
|
||||
|
||||
## 1. 探索阶段总结 (Current State Analysis)
|
||||
经过对代码库的探索,当前技能触发系统及相关配置状态如下:
|
||||
- `SkillSet.ts` 包含了所有技能的基座配置 `SkillConfig` 和技能字典 `SkillSet`。
|
||||
- `heroSet.ts` 中的 `HeroInfo` 存放英雄和怪物的配置,目前 `call`, `dead`, `fstart`, `fend` 被定义为 `number[]`,而 `atking` 和 `atked` 被定义为 `{s_uuid: number, t_num: number}[]`。
|
||||
- `HeroAttrsComp.ts` 内部存储了与配置一致的触发技能结构。
|
||||
- `SkillTriggerHelper.ts` 负责判定并向外派发 `GameEvent.TriggerSkill` 事件,由 `SCastSystem.ts` 监听并执行 `forceCastTriggerSkill`。
|
||||
- 目前 `SCastSystem.ts` 在收集技能目标和施放技能时,都是直接从 `SkillSet[s_uuid]` 读取 `config`,没有针对具体角色的差异化机制。
|
||||
|
||||
## 2. 拟议变更 (Proposed Changes)
|
||||
|
||||
### 2.1 修改 `SkillSet.ts`
|
||||
- **新增接口**:定义 `SkillOverrides` 接口,包含所有可被角色覆盖的技能参数(如 `TGroup`, `ap`, `hit_count`, `buffs` 等,全部为可选字段)。
|
||||
- **新增函数**:编写 `mergeSkillParams(config, overrides?)` 函数,将基座 `config` 和角色覆盖 `overrides` 进行合并,返回一个新的 `SkillConfig` 对象。
|
||||
|
||||
### 2.2 修改 `heroSet.ts`
|
||||
- **扩展接口**:
|
||||
- 在 `HSkillInfo` 接口中增加 `overrides?: SkillOverrides;`。
|
||||
- 将 `heroInfo` 接口中的触发字段 `call`, `dead`, `fstart`, `fend`, `atking`, `atked` 统一更新为 `{ s_uuid: number; t_num: number; overrides?: SkillOverrides }[]` 结构。
|
||||
- **更新配置示例**:按照设计文档更新英雄 `5001`, `5002`, `5301`, `5302` 的配置,为特定的触发技能添加 `overrides` 字段(如盾骑士 5002 全队护盾覆盖)。
|
||||
|
||||
### 2.3 修改 `HeroAttrsComp.ts`
|
||||
- **同步类型**:将 `call`, `dead`, `fstart`, `fend`, `atking`, `atked` 的类型同步改为与 `heroInfo` 一致的 `{ s_uuid: number; t_num: number; overrides?: SkillOverrides }[]`。
|
||||
|
||||
### 2.4 修改 `SkillTriggerHelper.ts`
|
||||
- **派发支持**:
|
||||
- 更新 `dispatchSingle` 方法签名,增加 `overrides?: SkillOverrides` 参数,并在 `oops.message.dispatchEvent(GameEvent.TriggerSkill, {...})` 中将其传入。
|
||||
- 更新 `handleCall`, `handleDead`, `handleArrayTrigger` 处理逻辑,将原先对 `number[]` 的处理改为对 `{s_uuid, t_num, overrides}` 对象数组的处理,并提取 `overrides` 传递给 `dispatchArray`。
|
||||
- 更新 `handleAtking`, `handleAtked` 中的 `dispatchSingle` 调用,传入 `atkConfig.overrides`。
|
||||
|
||||
### 2.5 修改 `SCastSystem.ts` (关键运行时逻辑)
|
||||
- **事件监听更新**:在 `onTriggerSkill` 方法的 `args` 参数定义中补充 `overrides?: SkillOverrides`,并传递给 `forceCastTriggerSkill`。
|
||||
- **合并逻辑上移(架构优化)**:
|
||||
- 在 `forceCastTriggerSkill` 和 `castSkill` 的**方法入口处**(而非 `applyFriendlySkillEffects` 内部),第一时间调用 `mergeSkillParams(config, overrides)` 获取 `effective` 技能配置。
|
||||
- 将后续所有关于阵营判定(如 `effective.TGroup`)、目标收集、以及传递给 `applyFriendlySkillEffects` / `applyEnemySkillEffects` 的参数全部替换为 `effective`。
|
||||
- **为何如此设计**:如果在原设计中仅在 `applyFriendlySkillEffects` 入口处合并,那么前置的**目标选择逻辑**(依赖 `TGroup` 判定是 `Self` 还是 `Team`)将会使用未合并的基础配置,导致类似“自己加盾变为全队加盾”的 `TGroup` 覆盖无法生效。将合并操作前置可以彻底解决这一问题。
|
||||
- **主动技能支持**:在 `pickCastSkill` 中,读取 `heroAttrs.skills[s_uuid]?.overrides` 并进行合并判定,同时将 `overrides` 放入返回的 `castPlan` 中,以便 `castSkill` 使用。
|
||||
|
||||
## 3. 假设与决策 (Assumptions & Decisions)
|
||||
- **统一触发结构**:虽然 `call`, `dead`, `fstart`, `fend` 不严格需要 `t_num`,但为了类型统一并完全遵守设计规范,统一采用了包含 `t_num` 的对象结构。
|
||||
- **合并前置决策**:如上所述,坚决在施放方法入口处进行 `mergeSkillParams` 以保证目标收集逻辑能够感知到 `TGroup` 的变化。这比设计规范中要求的修改范围略有扩大,但对于系统功能的正确实现是必须的。
|
||||
- **卡牌技能影响**:卡牌技能(`forceCastCardSkill`)当前没有绑定角色的 `overrides`,因此维持读取基础 `SkillSet` 逻辑不变。
|
||||
|
||||
## 4. 验证步骤 (Verification steps)
|
||||
1. 编译 TypeScript 代码,确保 `HeroAttrsComp`, `heroSet`, `SCastSystem` 等修改后的接口和类型无报错。
|
||||
2. 启动游戏或运行测试,确认 `5001` (见习战士) 触发的基础护盾只对自己生效。
|
||||
3. 确认 `5002` (盾骑士) 受击触发的护盾技能,正确地为全队附加护盾,并且护盾值(ap)与次数(hit_count)符合 `overrides` 配置。
|
||||
4. 确认所有旧版英雄技能在无 `overrides` 时能够正确回退到 `SkillSet` 的默认配置,游戏运转正常无异常日志。
|
||||
@@ -1,55 +0,0 @@
|
||||
# 重构场上英雄UI表现及交互计划
|
||||
|
||||
## 1. 目标与现状分析
|
||||
|
||||
**现状**:
|
||||
目前游戏中 `HInfoComp.ts` 负责在界面下方显示场上英雄的信息(生命、攻击、出售),由 `MissionCardComp` 管理 6 个固定槽位。
|
||||
`HeroViewComp.ts` 负责战斗场景中英雄实体的动画表现。
|
||||
|
||||
**目标**:
|
||||
1. 保留 `HInfoComp.ts` 组件及预制体,但**取消其在底部的常驻显示**,将其改造为**弹窗形式**(类似 `IBoxComp`)。
|
||||
2. 在战斗或准备阶段,玩家**直接点击场上的英雄模型**(`HeroViewComp`)时,弹出 `HInfoComp` 面板。
|
||||
3. 清理 `MissionCardComp.ts` 中管理底层 `HInfoComp` 的旧逻辑。
|
||||
|
||||
## 2. 具体修改步骤
|
||||
|
||||
### 2.1 注册 HInfo 为独立弹窗
|
||||
* 修改 `assets/script/game/common/config/GameUIConfig.ts`:
|
||||
* 在 `UIID` 枚举中添加 `HInfo`。
|
||||
* 在 `UIConfigData` 中注册:`[UIID.HInfo]: { layer: LayerType.UI, prefab: "gui/element/hnode" }`。
|
||||
|
||||
### 2.2 改造 HInfoComp.ts
|
||||
* **数据传入**:添加 `onAdded(args: { eid: number })`,根据 `eid` 查询 `HeroAttrsComp` 实体进行数据绑定。
|
||||
* **自驱动刷新**:原先由外部驱动刷新,现在添加 `update(dt: number)` 生命周期,在内部调用 `this.refresh()` 以保持血量等信息实时更新。
|
||||
* **移除旧逻辑**:删除 `node_index`、`refreshByNodeIndex` 等固定槽位相关的代码。
|
||||
* **交互恢复**:取消注释 `bindEvents` 和 `unbindEvents`,恢复出售按钮的点击事件。出售完成后调用 `oops.gui.remove(UIID.HInfo)`。打开 `IBox` 的点击逻辑可保持不变(或者作为详情按钮)。
|
||||
* **添加关闭机制**:考虑到它是弹窗,可以添加一个点击非按钮区域关闭自身的功能,或者点击英雄之外的区域关闭。为简单起见,可以暂时复用点击面板打开 IBox(同时关闭 HInfo),并在 HInfo 添加额外的关闭按钮,或由 UI 框架自动处理(如果注册为 PopUp 并带有背景)。如果它是纯 UI,可以点击其他地方关闭。这里我们让它在打开 `IBox` 后关闭自己:`oops.gui.remove(UIID.HInfo)`。
|
||||
|
||||
### 2.3 清理 MissionCardComp.ts
|
||||
* **移除属性**:删除 `@property(Node) hero_info_node` 和 `@property(Prefab) hero_info_prefab` 及其编辑器绑定。
|
||||
* **移除内部状态**:删除 `cachedHInfoComps` 和 `heroInfoSyncTimer`。
|
||||
* **移除生命周期调用**:在 `onLoad`、`update`、`onMissionStart`、`onMissionEnd`、`onDestroy`、`reset`、`enterPreparePhase`、`enterBattlePhase` 中,删除所有涉及 `HInfoComp` 实例创建、刷新、显隐控制、销毁的代码。
|
||||
|
||||
### 2.4 修改 HeroViewComp.ts 添加点击交互
|
||||
* **绑定事件**:在 `onLoad` 中为英雄模型节点绑定点击事件 `this.node.on(NodeEventType.TOUCH_END, this.onHeroClicked, this);`,并在 `reset` 等清理处解绑。
|
||||
* **点击回调逻辑**:
|
||||
```typescript
|
||||
private onHeroClicked(event: EventTouch) {
|
||||
if (!this.model) return;
|
||||
if (this.model.fac !== FacSet.HERO) return; // 仅对玩家英雄生效
|
||||
|
||||
const eid = this.ent?.eid;
|
||||
if (!eid) return;
|
||||
|
||||
// 呼出英雄信息弹窗
|
||||
oops.gui.remove(UIID.HInfo);
|
||||
oops.gui.open(UIID.HInfo, { eid: eid });
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 验证步骤
|
||||
1. 进入战斗,确认下方不再有常驻的英雄信息面板。
|
||||
2. 点击场上的英雄模型,确认能弹出该英雄的 `HInfoComp` 弹窗。
|
||||
3. 观察弹窗内的血量和攻击力是否能随战斗实时刷新。
|
||||
4. 点击弹窗上的出售按钮,确认英雄消失、金币增加且弹窗关闭。
|
||||
5. 点击弹窗上的信息区域,确认能弹出 `IBoxComp` 详情面板。
|
||||
247
.trae/documents/plan_spawn_flow_improvement.md
Normal file
247
.trae/documents/plan_spawn_flow_improvement.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# 刷怪心流改造 · 实施步骤与执行方案
|
||||
|
||||
> 基于对 RogueConfig.ts / MissionMonComp.ts / MissionComp.ts 的多 agent 调研与方案讨论产出。
|
||||
> 目标:让刷怪节奏产生"爽感"与心流(Flow),核心公式:**爽感 = 密度 × 可清性**,**心流 = 铺垫 → 峰值 → 释放循环 + 即时正反馈**。
|
||||
|
||||
---
|
||||
|
||||
## 阶段划分总览
|
||||
|
||||
```
|
||||
阶段一(P0 修复,4 项) → 安全网与曲线修正,不动体验结构
|
||||
阶段二(P1 节奏与 DDA,5 项) → 核心体验重塑,依赖链严格
|
||||
阶段三(P2-P3 反馈层,5 项) → 情绪反馈补全,可并行开发
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 阶段一:P0 修复
|
||||
|
||||
### Step 1.1 — P0-3 刷怪暂停死链路(先做:泄压阀)
|
||||
|
||||
**改动文件**:MissionMonComp.ts、MissionComp.ts
|
||||
|
||||
| # | 操作 | 位置 |
|
||||
|---|---|---|
|
||||
| 1 | `update()` 在 pending 统计之后、批次推进之前插入 `if (smc.mission.stop_spawn_mon) return;` | MissionMonComp L106 后 |
|
||||
| 2 | 阈值对齐:`maxMonsterCount` 80→60,`resumeMonsterCount` 45→40 | MissionComp L86-88 |
|
||||
| 3 | `changePhase(PrepareEnd)` 分支补 `smc.mission.stop_spawn_mon = false;`(防开局冻结 4 秒) | MissionComp L492-495 |
|
||||
|
||||
**验证**:临时 `maxMonsterCount=20` 进放松回合,确认到 20 只停刷、降 15 后续刷;wave 1 开局节奏无延迟。
|
||||
|
||||
**Commit**:`fix(mission): consume stop_spawn_mon in MissionMonComp update`
|
||||
|
||||
---
|
||||
|
||||
### Step 1.2 — P0-4 回合僵局超时(安全网)
|
||||
|
||||
**改动文件**:RogueConfig.ts、MissionComp.ts
|
||||
|
||||
| # | 操作 | 位置 |
|
||||
|---|---|---|
|
||||
| 1 | 新增常量 `WAVE_TIMEOUT = 75`(含 JSDoc) | RogueConfig 节奏常量区 |
|
||||
| 2 | MissionComp 新增字段 `skipRemainScoreOnBattleEnd`,`data_init` 复位 | MissionComp 运行时状态区 + L774 |
|
||||
| 3 | Battle 阶段 update 追加超时检测 `if (this.clearTime >= timeout) this.onBattleTimeout();`(timeout 取 `isBossWave ? 90 : WAVE_TIMEOUT`) | MissionComp L245-251 |
|
||||
| 4 | 新增 `onBattleTimeout()`:先扣 `wave_remain_monsters` → 销毁全部 MON 实体 → `mon_num=0` → 走 `TimeUpAdvanceWave` / `open_Victory` | MissionComp 回合管理区 |
|
||||
| 5 | BattleEnd 的 `wave_remain_monsters += mon_num` 加防重判断 | MissionComp L522-523 |
|
||||
|
||||
**验证**:临时 `WAVE_TIMEOUT=8`,确认 8 秒准点收束、评分已扣、factor 放水;TestModeConfig 高血怪模拟僵局验证 75s 前不结束。
|
||||
|
||||
**Commit**:`feat(mission): add wave battle timeout fallback`
|
||||
|
||||
---
|
||||
|
||||
### Step 1.3 — P0-1 Wave 1 错位修正
|
||||
|
||||
**改动文件**:RogueConfig.ts(单文件)
|
||||
|
||||
| # | 操作 | 位置 |
|
||||
|---|---|---|
|
||||
| 1 | `getWaveType`:`wave % 5 === 1` → `wave % 5 === 4` | L80 |
|
||||
| 2 | 同步 4 处注释:WaveType 枚举注释(L37)、文件头节奏注释(L21-23)、WaveConfigs 表头注释(L329-332)、表内"放松回合"行内注释从 wave 6/11/16 搬到 4/9/14 | 见左 |
|
||||
|
||||
**验证**:`getWaveType(1..20)` 断言输出 `N N N R P` × 4 循环;实机确认 wave 1 = 18 只、wave 4 ≈ 40 只低强度。
|
||||
|
||||
**Commit**:`fix(rogue): move relax wave to pre-boss slot (wave%5==4)`
|
||||
|
||||
---
|
||||
|
||||
### Step 1.4 — P0-2 Boss 压轴 + 预警
|
||||
|
||||
**改动文件**:RogueConfig.ts、MissionComp.ts、GameEvent.ts
|
||||
|
||||
| # | 操作 | 位置 |
|
||||
|---|---|---|
|
||||
| 1 | `SquadLibrary` 新增 `boss_guard`(weight:0,1 重甲+2 近战) | RogueConfig L191-198 |
|
||||
| 2 | `GeneratedMonster` 增加可选字段 `isBossGuard?: boolean` | RogueConfig L426-450 |
|
||||
| 3 | `generateWave` 重构第 3/4/8 步:Boss 不 push 首位 → 小队拼装 → 护卫队 `makeBossGuards()` → Boss 压队尾 → 批次统一分配后强制 Boss+护卫 `batch = BATCH_COUNT-1`;`remaining` 改为 `-= 4` | RogueConfig L509-554 |
|
||||
| 4 | `makeBoss` 的 `spawnIndex:0, batch:0` 改为占位默认值+注释 | RogueConfig L660-687 |
|
||||
| 5 | GameEvent 新增 `BossWarning = "BossWarning"` | GameEvent.ts |
|
||||
| 6 | `changePhase(BattleStart)`:`if (this.isBossWave)` 派发 `BossWarning { wave, eta: 20 }` | MissionComp L497-500 |
|
||||
| 7 | MissionComp 从 RogueConfig 补导入 `BATCH_INTERVAL, BATCH_COUNT` | MissionComp L51 |
|
||||
|
||||
**验证**:wave 5 确认 0s/10s 两批纯杂兵、20s Boss 带 3 护卫进场、总怪数=21;`BossWarning` 仅在 5/10/15/20 派发。
|
||||
|
||||
**Commit**:`feat(rogue): spawn boss in final batch with guards and warning event`
|
||||
|
||||
---
|
||||
|
||||
## 阶段二:P1 节奏与 DDA(依赖链严格)
|
||||
|
||||
依赖链:**P1-5 → P1-1 → P1-2 + P1-3(同批)→ P1-4**
|
||||
|
||||
### Step 2.1 — P1-5 数值语义澄清(等价变换先行)
|
||||
|
||||
**改动文件**:RogueConfig.ts(单文件)
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | 删除第 5 步(乘 hp_mul/ap_mul),第 6 步改为拆分 `hpScale = targetPower × hp_mul / totalBasePower`、`apScale = targetPower × ap_mul / totalBasePower` |
|
||||
| 2 | 更新文件头公式注释(L12-16)与 `hp_mul/ap_mul` 字段注释 |
|
||||
| 3 | 17/18/19 启用 `power_adjust: 1.05 / 1.10 / 1.20` |
|
||||
| 4 | `validateRogueConfig` 增加 `power_adjust ∈ [0.8, 1.3]` 校验 |
|
||||
|
||||
**验证**:改造前后固定 heroPower 打桩,逐怪 hp/ap 断言相等(等价回归);17/18/19 总强度阶梯断言。
|
||||
|
||||
**Commit**:`refactor(rogue): unify hp/ap mul into power scaling formula`
|
||||
|
||||
---
|
||||
|
||||
### Step 2.2 — P1-1 批次递增 + 间隔参数化
|
||||
|
||||
**改动文件**:RogueConfig.ts、MissionMonComp.ts
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | 新增 `BATCH_RATIO = [0.25, 0.35, 0.40]`、`FINALE_SQUAD_COUNT = 2`、`SPAWN_INTERVAL_BY_TYPE`(Normal 0.18 / Pressure 0.25 / Relax 0.12) |
|
||||
| 2 | 批次分配从 `i % 3` 改为 `assignBatches()`:配额切分 + 第三批补 2 个最强小队(`pickStrongestSquad` 按 `calcHeroPower(样本)×count` 评分);放松回合跳过收尾加压;`totalCount` 预留收尾小队名额防 `slice` 截掉 |
|
||||
| 3 | MissionMonComp:`MON_SPAWN_INTERVAL` 静态常量改为实例字段 `spawnInterval`,`onPhasePrepareEnd` 按 `getWaveType(currentWave)` 查表;`releaseCurrentBatch` 末尾的 spawnTimer 初始化同步改 |
|
||||
|
||||
**与 P0-2 的合并点**:`assignBatches` 需保留"Boss+护卫强制最后一批"逻辑。
|
||||
|
||||
**验证**:debugMode 日志打印每批只数与最强小队 id;放松回合 54 只应 ~6.5s 倾泻完。
|
||||
|
||||
**Commit**:`feat(rogue): progressive batch ratio and per-type spawn interval`
|
||||
|
||||
---
|
||||
|
||||
### Step 2.3 — P1-2 + P1-3 清场加速与 DDA 重做(**必须同 commit**)
|
||||
|
||||
**改动文件**:RogueConfig.ts、MissionMonComp.ts、MissionComp.ts、GameSet.ts
|
||||
|
||||
P1-2 部分(MissionMonComp):
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | 新增状态:`batchReleasedCount`、`batchFastForwarded`、`aliveCheckTimer`、`waveEarlySkipTotal`(public 只读) |
|
||||
| 2 | `releaseCurrentBatch` 记录当批数量;update 中 0.2s 节流调 `checkBatchEarlyAdvance()` |
|
||||
| 3 | `checkBatchEarlyAdvance`:存活比例 ≤25% 且本批已放完 → `batchTimer += 2s`、`waveEarlySkipTotal += 2`(每批一次) |
|
||||
|
||||
P1-3 部分(RogueConfig + MissionComp + GameSet):
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | `DynamicTuner` 重写:连续映射 `desired = 1 + (0.8 - clearTime/30) × 0.5`(死亡锚定 0.8)、滞回 2 回合、指数靠拢 50%、区间 [0.7, 1.3]、新增 `streak` 字段 |
|
||||
| 2 | MissionComp BattleEnd:`effectiveClearTime = clearTime + MonComp.waveEarlySkipTotal` 后传入 adjust;顺手写入 `lastWaveDeathCount`/`lastWaveClearTime` 快照(供 Step 2.4) |
|
||||
| 3 | `FightSet.WAVE_HEAL_RATE` 0.5→0.4;修正 MissionComp L676"恢复70%"幽灵注释 |
|
||||
|
||||
**验证**:脚本模拟三种 clearTime 曲线(恒 12s / 恒 25s / 交替)打印 20 回合 factor 轨迹;强 build 单回合时长应从 ~30s 压到 ~24-26s。
|
||||
|
||||
**Commit**:`feat(rogue): early batch advance and continuous DDA with hysteresis`
|
||||
|
||||
---
|
||||
|
||||
### Step 2.4 — P1-4 动态回合倒计时
|
||||
|
||||
**改动文件**:MissionComp.ts(单文件)
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | 新增档位常量 `COUNTDOWN_FAST=2.5 / NORMAL=4.0 / FULL=5.0`,删除/降级 `WAVE_COUNTDOWN_DURATION` |
|
||||
| 2 | `startWaveCountdown` 改调 `computeCountdown()`:有死亡→5s,clearTime<65%→2.5s,否则 4s(读 Step 2.3 写入的快照字段) |
|
||||
|
||||
**验证**:快清场回合倒计时显示 3→2→1;有死亡回合给满 5s。
|
||||
|
||||
**Commit**:`feat(mission): adaptive wave countdown based on last wave performance`
|
||||
|
||||
---
|
||||
|
||||
## 阶段三:P2-P3 反馈层
|
||||
|
||||
### Step 3.1 — 事件与工具基建 + P2-2 清屏庆祝 + P2-3 Boss 演出
|
||||
|
||||
**改动文件**:GameEvent.ts、MissionComp.ts、MissionMonComp.ts、**新增** ScreenShake.ts、**新增** BattleBannerComp.ts、mission.prefab(编辑器)
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | GameEvent 新增:`WaveClear` / `BossWarning`(Step 1.4 已加则跳过)/ `BossSpawn` / `CoinFly` / `ComboReach`,启用 `MonDead` |
|
||||
| 2 | 新增 `ScreenShake` 静态工具(抖 `smc.map.MapView.node`,强度/时长参数,收敛回原位) |
|
||||
| 3 | 新增 `BattleBannerComp`:统一横幅通道(右进 backOut → 停留 → 左出 backIn),监听 WaveClear/BossWarning/ComboReach,0.3s 去抖 |
|
||||
| 4 | MissionComp 清屏分支 dispatch `WaveClear { wave, clearTime, allAlive, fastClear }`(通关分支不派发) |
|
||||
| 5 | MissionMonComp `addMonsterAtGrid` isBoss 分支 dispatch `BossSpawn { pos }` |
|
||||
| 6 | 奖励规则:fastClear=1 → +2 金,=2 → +5 金,allAlive → +1 刷新石(走 MissionEconomy) |
|
||||
| 7 | **编辑器操作**:mission.prefab 新增 `banner` 节点(Label+背景),挂 BattleBannerComp |
|
||||
|
||||
**Commit**:`feat(battle): wave clear banner, boss warning and screen shake`
|
||||
|
||||
---
|
||||
|
||||
### Step 3.2 — P2-1 波次 HUD + P2-4 金币飞行
|
||||
|
||||
**改动文件**:**新增** WaveHudComp.ts、**新增** CoinFlyComp.ts、HeroAtkSystem.ts、mission.prefab(编辑器)
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | `WaveHudComp`:监听 NewWave 缓存 total;0.2s 轮询 `mon_num + pending_mon_num` 刷新进度条;静态生成 20 格旗帜(`getWaveType(i)===Pressure` 置 Boss 帧),当前回合脉动 |
|
||||
| 2 | HeroAtkSystem `scheduleDrop` 后 dispatch `CoinFly { worldPos, gold, isBoss }`;`onDeath` MON 分支 dispatch `MonDead` |
|
||||
| 3 | `CoinFlyComp`:独立 NodePool(上限 30),3-5 枚(Boss 8 枚)金币抛物线散开→加速飞向钱包;金币帧运行时取编辑器绑定的 `coinIconSrc.spriteFrame`;**账务立即结算、飞行纯表现** |
|
||||
| 4 | **编辑器操作**:prefab 新增 `wave_hud`(wave_lab/remain_lab/progress/flags)与 `fly_layer`(最高 sibling),挂组件并拖绑定 |
|
||||
|
||||
**Commit**:`feat(battle): wave progress HUD and coin fly animation`
|
||||
|
||||
---
|
||||
|
||||
### Step 3.3 — P3-1 连杀 Combo
|
||||
|
||||
**改动文件**:**新增** ComboComp.ts、mission.prefab(编辑器)
|
||||
|
||||
| # | 操作 |
|
||||
|---|---|
|
||||
| 1 | `ComboComp`:监听 MonDead,2s 滑动窗口计数,5/10/20 阈值派发 `ComboReach { count, tier }`;MissionEnd 清零 |
|
||||
| 2 | BattleBannerComp 消费 ComboReach:分级文案/颜色/震屏(tier2 `shake(8,0.3)`)+ 金币爆发 2/5/10 |
|
||||
| 3 | **编辑器操作**:prefab 挂 ComboComp |
|
||||
|
||||
**Commit**:`feat(battle): kill combo system with tiered rewards`
|
||||
|
||||
---
|
||||
|
||||
## 执行约束
|
||||
|
||||
**每次 commit 前必做**:
|
||||
1. `validateRogueConfig()` 返回空数组
|
||||
2. GetDiagnostics 检查改动文件无 TS 错误
|
||||
3. 手动验证项按各 Step 的验证清单执行
|
||||
|
||||
**Commit message** 遵循项目规范(Conventional Commits、英文、祈使句、≤50 字符)。
|
||||
|
||||
**资源依赖**(不阻塞开发,可后期补):Boss 旗帜帧 ×1、清屏/Boss 预警/连杀音效 ×5、金币入袋音效 ×1——占位方案:flash/dun/Hit/Critical/Fire/button 复用。
|
||||
|
||||
**风险最高的两步**:Step 1.1(PrepareEnd 时序坑)和 Step 2.3(P1-2/P1-3 必须同批)。实施时优先单独验证这两点。
|
||||
|
||||
---
|
||||
|
||||
## 附录:关键数值速查
|
||||
|
||||
| 参数 | 现值 | 目标值 | 所属 Step |
|
||||
|---|---|---|---|
|
||||
| getWaveType Relax 位 | wave%5==1 | wave%5==4 | 1.3 |
|
||||
| Boss 批次 | batch 0 | batch 2 + 3 护卫 | 1.4 |
|
||||
| maxMonsterCount / resume | 80 / 45 | 60 / 40 | 1.1 |
|
||||
| WAVE_TIMEOUT | 无 | 75s(Boss 90s) | 1.2 |
|
||||
| BATCH_RATIO | 33/33/33 | 25/35/40 + 收尾小队×2 | 2.2 |
|
||||
| 刷怪间隔 | 0.3s 统一 | Normal 0.18 / Pressure 0.25 / Relax 0.12 | 2.2 |
|
||||
| 清场加速 | 无 | 存活≤25% 时 batchTimer += 2s | 2.3 |
|
||||
| DynamicTuner | ±0.05 步进 [0.5, 2.0] | 连续映射+滞回 [0.7, 1.3] | 2.3 |
|
||||
| WAVE_HEAL_RATE | 0.5 | 0.4 | 2.3 |
|
||||
| 回合间倒计时 | 固定 5s | 2.5 / 4 / 5 三档 | 2.4 |
|
||||
| power_adjust 17/18/19 | 未使用 | 1.05 / 1.10 / 1.20 | 2.1 |
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,10 +38,13 @@
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 110
|
||||
},
|
||||
{
|
||||
"__id__": 112
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 112
|
||||
"__id__": 114
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -95,7 +98,7 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 9.114,
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
"z": 0
|
||||
},
|
||||
@@ -217,7 +220,7 @@
|
||||
"__id__": 17
|
||||
}
|
||||
],
|
||||
"_active": false,
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 25
|
||||
@@ -235,7 +238,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -9.738,
|
||||
"y": -7.84,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -287,7 +290,7 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -35,
|
||||
"x": -20,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
@@ -328,8 +331,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 70,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 5
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -506,8 +509,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 240,
|
||||
"height": 40
|
||||
"width": 140,
|
||||
"height": 23.333333333333336
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -631,8 +634,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 70,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 5
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -859,7 +862,7 @@
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 40,
|
||||
"height": 11
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1037,7 +1040,7 @@
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 140,
|
||||
"height": 43.333333333333336
|
||||
"height": 36.66666666666667
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1162,7 +1165,7 @@
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 40,
|
||||
"height": 11
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1347,7 +1350,7 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -25,
|
||||
"x": -20,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
@@ -1388,8 +1391,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 50,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1691,8 +1694,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 50,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1813,7 +1816,7 @@
|
||||
"__id__": 99
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_active": false,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 105
|
||||
@@ -2520,8 +2523,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 70,
|
||||
"height": 20
|
||||
"width": 40,
|
||||
"height": 11
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -2534,6 +2537,33 @@
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "cdvV5hAkdEaY6AT6urHKuf"
|
||||
},
|
||||
{
|
||||
"__type__": "c4181/FEWhKbY0D0PLdCyKi",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 113
|
||||
},
|
||||
"hpProgressBar": {
|
||||
"__id__": 53
|
||||
},
|
||||
"lvLabel": null,
|
||||
"shieldProgressBar": {
|
||||
"__id__": 77
|
||||
},
|
||||
"hpBarSprite": {
|
||||
"__id__": 36
|
||||
},
|
||||
"_id": "a77QVRkP5NX4b+LyqmW1KJ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "3eCKBFOJhLHKqcnak/mi3d"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
|
||||
@@ -1398,7 +1398,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21.504,
|
||||
"y": -31.947,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -1875,7 +1875,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21.504,
|
||||
"y": -50.813,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -2352,7 +2352,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"y": -150,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -2618,6 +2618,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "6bE0ieRMBGyYHdxvuJ03FA",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -3432,6 +3434,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "256TxMVzhHQopA08O7v8nq",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -3471,6 +3475,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "5dWwAe3IFHRJuoSicXBEs8",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -3710,6 +3716,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "b9zLdCeJlE5pPiK++dLgBX",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -4388,6 +4396,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "9d2p/canpFGof2O/w8JyTj",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -4427,6 +4437,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "6bkL20JyRDOI6ycVYKl/ie",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -4495,6 +4507,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "14GnCNU65BbJPIzLWE/Oiz",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -7363,6 +7377,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c4IqMKgXNExa5w4EAciRxE",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -8202,7 +8218,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "LINE2",
|
||||
"_name": "BOSS",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8273,7 +8289,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ea767WaZRHo4s0bZQfN8zP"
|
||||
"fileId": "3fVB8r2UFAB5QujD6LOw6s"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8283,14 +8299,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "60UdPtJn9ALLHCveLwT1bT",
|
||||
"fileId": "f4iFaXCXNKorfGJFH32POa",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "HERO",
|
||||
"_name": "MON",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8361,7 +8377,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "dasmpuTSVM7qA3QW9/3XjQ"
|
||||
"fileId": "ea767WaZRHo4s0bZQfN8zP"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8371,14 +8387,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "eaiasKNEFO6qvodGhL4a0b",
|
||||
"fileId": "60UdPtJn9ALLHCveLwT1bT",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "LINE1",
|
||||
"_name": "HERO",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8449,7 +8465,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b8C5CFg5lM4oUHMeSrc/sV"
|
||||
"fileId": "dasmpuTSVM7qA3QW9/3XjQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8459,14 +8475,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "4dcNJaDM1CLI9q3fIqHZ/U",
|
||||
"fileId": "eaiasKNEFO6qvodGhL4a0b",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "LINE3",
|
||||
"_name": "LINE1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8537,7 +8553,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "a5LMIIbHtOKK10K5do5dSQ"
|
||||
"fileId": "b8C5CFg5lM4oUHMeSrc/sV"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8547,14 +8563,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "a6CBSkrzZIurlX0D/HzXDp",
|
||||
"fileId": "4dcNJaDM1CLI9q3fIqHZ/U",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "BOOS",
|
||||
"_name": "LINE3",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8625,7 +8641,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "3fVB8r2UFAB5QujD6LOw6s"
|
||||
"fileId": "a5LMIIbHtOKK10K5do5dSQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8635,7 +8651,7 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "f4iFaXCXNKorfGJFH32POa",
|
||||
"fileId": "a6CBSkrzZIurlX0D/HzXDp",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
@@ -8893,6 +8909,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "35iAcUUYpE8akTtTCX1ITo",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -8932,6 +8950,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "68Q5vEQWBINrggngtTZ7wh",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,21 +7,255 @@
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 8,
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 2,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0,
|
||||
"_duration": 0.125,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [],
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [],
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 1
|
||||
"__id__": 12
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.VectorTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channels": [
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
{
|
||||
"__id__": 6
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_nComponents": 3
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
"scale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.08333333333333333,
|
||||
0.125
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.2999999523162842,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.2999999523162842,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"tangentMode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.2999999523162842,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.08333333333333333,
|
||||
0.125
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.100000023841858,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.100000023841858,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"tangentMode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.100000023841858,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.08333333333333333,
|
||||
0.125
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"tangentMode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [],
|
||||
"_values": [],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
|
||||
116
assets/resources/game/skill/anm/atk/atk02.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk02.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk02.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk02.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk02"
|
||||
}
|
||||
}
|
||||
116
assets/resources/game/skill/anm/atk/atk03.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk03.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk03.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk03.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk03"
|
||||
}
|
||||
}
|
||||
116
assets/resources/game/skill/anm/atk/atk04.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk04.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk04.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk04.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk04"
|
||||
}
|
||||
}
|
||||
116
assets/resources/game/skill/anm/atk/atk05.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk05.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk05.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk05.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk05"
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk",
|
||||
"_name": "atk1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk",
|
||||
"_name": "atk1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
@@ -108,8 +108,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -0.5,
|
||||
"y": 0.5,
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -136,8 +136,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 139,
|
||||
"height": 128
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -186,7 +186,7 @@
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
@@ -222,8 +222,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 50
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -248,8 +248,13 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"distance": 100,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
@@ -315,8 +320,8 @@
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 50
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
@@ -336,7 +341,7 @@
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": false,
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "45017156-064b-43f2-a24e-176b6ce17ad8",
|
||||
@@ -8,6 +8,6 @@
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk"
|
||||
"syncNodeName": "atk1"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk2.prefab
Normal file
375
assets/resources/game/skill/atk/atk2.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk2",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk2",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk2.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk2.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "ff6e9a75-b1b9-448e-9cbd-d30de98fd21b",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk2"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk3.prefab
Normal file
375
assets/resources/game/skill/atk/atk3.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk3",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk3",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk3.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk3.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "16ca6f33-f910-4202-add4-2862be6c9127",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk3"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk4.prefab
Normal file
375
assets/resources/game/skill/atk/atk4.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk4",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk4",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk4.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk4.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "7f30cd3c-57d1-476a-96bb-02c4eb082576",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk4"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk5.prefab
Normal file
375
assets/resources/game/skill/atk/atk5.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk5",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk5",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk5.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk5.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "7227b42b-9cc7-41bd-8858-9d05c4cc758b",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk5"
|
||||
}
|
||||
}
|
||||
@@ -108,8 +108,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.9,
|
||||
"y": 0.9,
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -282,7 +282,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 25,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -108,8 +108,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.9,
|
||||
"y": 0.9,
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -282,7 +282,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 25,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -108,8 +108,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.8,
|
||||
"y": 0.8,
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -282,7 +282,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 25,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.35,
|
||||
"y": 0.35,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.35,
|
||||
"y": 0.35,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -253,7 +253,11 @@
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -389,7 +389,11 @@
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -504,4 +508,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.4,
|
||||
"y": 0.4,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 926 KiB After Width: | Height: | Size: 982 KiB |
@@ -4746,7 +4746,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 22.263000000000016,
|
||||
"y": 14.014,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -232,7 +232,7 @@
|
||||
"_top": 0,
|
||||
"_bottom": 546.004,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 143,
|
||||
"_verticalCenter": -7,
|
||||
"_isAbsLeft": true,
|
||||
"_isAbsRight": true,
|
||||
"_isAbsTop": true,
|
||||
@@ -276,7 +276,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 783,
|
||||
"y": 633,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5958,10 +5958,10 @@
|
||||
"height": 61,
|
||||
"rawWidth": 54,
|
||||
"rawHeight": 61,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"borderTop": 28,
|
||||
"borderBottom": 33,
|
||||
"borderLeft": 27,
|
||||
"borderRight": 27,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
@@ -6006,8 +6006,8 @@
|
||||
"rawHeight": 24,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"borderLeft": 25,
|
||||
"borderRight": 25,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
|
||||
@@ -30,7 +30,7 @@ export function getCardIconId(cardData: CardConfig | null): string {
|
||||
|
||||
// 2. 装备卡:取第一个 field 词条图标
|
||||
if (cardData.field && cardData.field.length > 0) {
|
||||
const fieldUuid = cardData.field[0];
|
||||
const fieldUuid = cardData.field[0].uuid;
|
||||
return FieldSkillSet[fieldUuid]?.icon || `${fieldUuid}`;
|
||||
}
|
||||
|
||||
|
||||
45
assets/script/game/common/ScreenShake.ts
Normal file
45
assets/script/game/common/ScreenShake.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @file ScreenShake.ts
|
||||
* @description 屏幕震动静态工具(表现层)
|
||||
*
|
||||
* 项目内无现成相机震动能力(MapViewScene.camera 可能为 null 且从未使用),
|
||||
* 改为抖动地图根节点 smc.map.MapView.node(地图+实体一起震,UI 层不受影响)。
|
||||
*
|
||||
* 用法:
|
||||
* ScreenShake.shake(10, 0.4); // 强度 10 像素,持续 0.4 秒
|
||||
*/
|
||||
import { tween, Tween, v3, Vec3 } from "cc";
|
||||
import { smc } from "./SingletonModuleComp";
|
||||
|
||||
export class ScreenShake {
|
||||
/** 地图根节点的基准位置(首次震动时快照,用于收敛回原位,防多次震动漂移) */
|
||||
private static basePos: Vec3 | null = null;
|
||||
|
||||
/**
|
||||
* 触发一次屏幕震动
|
||||
* @param strength 像素振幅(建议 4~10)
|
||||
* @param duration 总时长(秒,建议 0.2~0.4)
|
||||
*/
|
||||
static shake(strength: number = 8, duration: number = 0.3) {
|
||||
const target = smc.map?.MapView?.node;
|
||||
if (!target || !target.isValid) return;
|
||||
|
||||
if (!this.basePos) this.basePos = target.position.clone();
|
||||
|
||||
// 打断进行中的震动(新震动覆盖旧震动,不做强度叠加,避免失控)
|
||||
Tween.stopAllByTarget(target);
|
||||
|
||||
const steps = Math.max(1, Math.floor(duration / 0.04));
|
||||
const t = tween(target);
|
||||
for (let i = 0; i < steps; i++) {
|
||||
t.to(0.04, {
|
||||
position: v3(
|
||||
this.basePos.x + (Math.random() * 2 - 1) * strength,
|
||||
this.basePos.y + (Math.random() * 2 - 1) * strength,
|
||||
this.basePos.z),
|
||||
});
|
||||
}
|
||||
// 收敛回原位
|
||||
t.to(0.05, { position: this.basePos }).start();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a3113d69-645e-4a4b-bf68-a434fcbd6d8d",
|
||||
"uuid": "a7e31780-029a-4f20-90ad-3e5575c9dac1",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
@@ -38,6 +38,23 @@ export interface CloudData {
|
||||
openid: string;
|
||||
data: GameDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 死亡英雄快照:英雄真实死亡时记录,供后续复活机制(重新召唤)消费。
|
||||
* 仅记录静态/成长数据,不含战斗内临时状态(buff、CD 等)。
|
||||
*/
|
||||
export interface DeadHeroRecord {
|
||||
/** 英雄模板 uuid(决定重新召唤哪个英雄) */
|
||||
uuid: number;
|
||||
/** 死亡时的英雄等级(复活后继承) */
|
||||
lv: number;
|
||||
/** 卡牌等级(驱动 bg_node 颜色) */
|
||||
card_lv: number;
|
||||
/** 死亡时占用的站位索引(复活时优先归位,-1 表示未分配) */
|
||||
posIndex: number;
|
||||
/** 死亡时的回合数(用于统计/按回合清理) */
|
||||
wave: number;
|
||||
}
|
||||
/** 游戏模块 */
|
||||
@ecs.register('SingletonModule')
|
||||
export class SingletonModuleComp extends ecs.Comp {
|
||||
@@ -58,8 +75,10 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
in_select: false,
|
||||
in_fight: false,
|
||||
stop_mon_action: false,
|
||||
heroGrid: [-1, -1, -1, -1, -1, -1],
|
||||
heroGrid: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
|
||||
monGrid: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
|
||||
/** 本局真实死亡的英雄快照列表,供后续复活机制消费;整局结束(MissionEnd)时清空 */
|
||||
dead_heroes: [] as DeadHeroRecord[],
|
||||
};
|
||||
data: any = {
|
||||
openid: '',
|
||||
@@ -85,6 +104,8 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
game_pause: false,
|
||||
mission_data: {
|
||||
mon_num: 0,//怪物数量
|
||||
pending_mon_num: 0,//待刷出怪物数量(MissionMonComp 每帧写入,供回合结束检测与 HUD 进度)
|
||||
wave_early_skip: 0,//本回合清场加速累计节省的秒数(MissionComp 用于还原 DDA 判定口径)
|
||||
hero_num: 0,//英雄数量
|
||||
hero_max_num: FightSet.HERO_MAX_NUM,//英雄可召唤上限
|
||||
hero_extend_max_num: FightSet.HERO_MAX_NUM + 1,//英雄可拓展上限
|
||||
|
||||
@@ -104,12 +104,12 @@ export const BuffList: Record<number, BuffConfig> = {
|
||||
modifiers: [{ attr: Attrs.critical, op: ModOp.Flat, value: 30 }],
|
||||
info: "暴击率提升 30%,持续 5 秒",
|
||||
},
|
||||
// 击退概率强化(限时)
|
||||
// 击退距离强化(限时)
|
||||
7013: {
|
||||
id: 7013, name: "击退爆发", icon: "Stat_Stun_01",
|
||||
category: BuffCategory.Buff, duration: 5, max_stack: 1,
|
||||
modifiers: [{ attr: Attrs.knockback_chance, op: ModOp.Flat, value: 30 }],
|
||||
info: "击退概率提升 30%,持续 5 秒",
|
||||
info: "击退距离提升 30,持续 5 秒",
|
||||
},
|
||||
// 穿透强化(限时)
|
||||
7014: {
|
||||
@@ -167,4 +167,11 @@ export const BuffList: Record<number, BuffConfig> = {
|
||||
modifiers: [{ attr: Attrs.freeze_chance, op: ModOp.Flat, value: 5 }],
|
||||
info: "冰冻概率提升 5%,持续 5 秒",
|
||||
},
|
||||
// 穿透伤害强化(限时)
|
||||
7022: {
|
||||
id: 7022, name: "穿透伤害爆发", icon: "Stat_Tripleshot",
|
||||
category: BuffCategory.Buff, duration: 5, max_stack: 1,
|
||||
modifiers: [{ attr: Attrs.puncture_dmg_bonus, op: ModOp.Flat, value: 30 }],
|
||||
info: "穿透后伤害提升 30%,持续 5 秒",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as exp from "constants"
|
||||
import { HeroInfo, HeroList, HType } from "./heroSet"
|
||||
import { HeroInfo, HeroList, HType, FieldEntry } from "./heroSet"
|
||||
import { oops } from "db://oops-framework/core/Oops"
|
||||
import { SkillOverrides, TGroup } from "./SkillSet"
|
||||
|
||||
@@ -46,7 +46,6 @@ export enum CardTriggerType {
|
||||
Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发
|
||||
Field = 3, // 驻场光环(俗称「装备」):被动生效,由 field 数组驱动,支持单卡多词条
|
||||
FightStart = 4, // 战斗开始时触发
|
||||
FightEnd = 5, // 战斗结束时触发(每回合结束)
|
||||
HeroDead = 6, // 场上己方英雄死亡时触发
|
||||
HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活)
|
||||
}
|
||||
@@ -73,12 +72,12 @@ export interface CardConfig {
|
||||
t_inv?: number // 触发间隔(秒)
|
||||
keep_waves?: number // 维持的回合数(-1表示持续到战斗结束,0或undefined表示仅本回合)
|
||||
overrides?: SkillOverrides // 技能参数覆写(如自定义伤害ap、buff值、金币数等)
|
||||
field?: number[] // 装备词条数组:驻场光环卡(trigger_type=Field)的属性词条,支持单卡多词条(如 [7002, 7004, 7005] 同时提供攻击/暴击/暴伤)
|
||||
field?: FieldEntry[] // 装备词条数组:驻场光环卡(trigger_type=Field)的属性词条,支持单卡多词条;value 为实际生效数值(如 [{uuid:7002,value:0.1},{uuid:7004,value:0.1}] 同时提供攻击/暴击)
|
||||
|
||||
/** 触发类型(必填,技能卡专用;功能卡/英雄卡可缺省) */
|
||||
trigger_type?: CardTriggerType;
|
||||
/**
|
||||
* 事件型触发的全局次数上限(仅 FightStart/FightEnd/HeroDead/HeroCall 有效)
|
||||
* 事件型触发的全局次数上限(仅 FightStart/HeroDead/HeroCall 有效)
|
||||
* 默认 Infinity;达到上限后销毁节点
|
||||
* 注意:与 t_times 语义不同——t_times 控制每回合内 Interval 的次数
|
||||
*/
|
||||
@@ -102,17 +101,26 @@ function getHeroCost(cardLv: number): number {
|
||||
return 15;
|
||||
}
|
||||
|
||||
/**
|
||||
* 英雄卡按 card_lv 分配抽取权重(几何递减,高等级越稀有)。
|
||||
* 梯度:lv1=40, lv2=25, lv3=15, lv4=8, lv5=3
|
||||
*/
|
||||
const HERO_WEIGHT_BY_LV: Record<number, number> = {
|
||||
1: 40, 2: 25, 3: 15, 4: 8, 5: 3,
|
||||
};
|
||||
function getHeroWeight(cardLv: number): number {
|
||||
return HERO_WEIGHT_BY_LV[cardLv] ?? HERO_WEIGHT_BY_LV[1];
|
||||
}
|
||||
|
||||
HeroList.forEach(uuid => {
|
||||
const hero = HeroInfo[uuid];
|
||||
if (!hero) return;
|
||||
|
||||
const baseWeight = 25;
|
||||
|
||||
CardPoolList.push({
|
||||
uuid: hero.uuid,
|
||||
type: CardType.Hero,
|
||||
cost: getHeroCost(hero.card_lv ?? 1),
|
||||
weight: baseWeight,
|
||||
weight: getHeroWeight(hero.card_lv ?? 1),
|
||||
card_lv: hero.card_lv ?? 1,
|
||||
kind: CKind.Hero,
|
||||
hero_lv: 1,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* - FieldSkillSet(SkillSet)—— 属性词条配置
|
||||
*/
|
||||
import { CardConfig, CardType, CKind, CardTriggerType } from "./CardSet";
|
||||
import { FieldEntry } from "./heroSet";
|
||||
|
||||
/** 装备卡原始数据(仅装备相关字段,由 EquipSet 内部转换为 CardConfig) */
|
||||
interface EquipRawData {
|
||||
@@ -23,63 +24,65 @@ interface EquipRawData {
|
||||
name: string;
|
||||
info: string;
|
||||
icon?: string; // 图标ID(对应 ui6 图集 frame 名)
|
||||
field: number[];
|
||||
field: FieldEntry[]; // 驻场光环词条:uuid 指向 FieldSkillSet 底座,value 为实际生效数值
|
||||
cost?: number;
|
||||
}
|
||||
|
||||
/** 装备卡原始数据表 */
|
||||
const EquipRawList: EquipRawData[] = [
|
||||
// ==================== card_lv 1 档(基础强度) ====================
|
||||
{ uuid: 8701, card_lv: 1, icon: "Helmet-FA_Helmet_021_Gray", name: "战后恢复", info: "战斗结束生命回复量+10%", field: [7001] },
|
||||
{ uuid: 8702, card_lv: 1, icon: "Sword-FA_WP_Main_Sword_003_GrayGold", name: "攻击加成", info: "全体攻击力+10%", field: [7002] },
|
||||
{ uuid: 8703, card_lv: 1, icon: "Blunt-FA_WP_Main_Blunt_002_Wood", name: "击晕加成", info: "全体击晕概率+10%", field: [7003] },
|
||||
{ uuid: 8704, card_lv: 1, icon: "Axe-FA_WP_Main_Axe_001_WoodGray", name: "暴击加成", info: "全体暴击率+10%", field: [7004] },
|
||||
{ uuid: 8705, card_lv: 1, icon: "Bow-FA_WP_Main_Bow_003_WoodGreen", name: "暴伤加成", info: "全体暴击伤害+20%", field: [7005] },
|
||||
{ uuid: 8706, card_lv: 1, icon: "Crossbow-FA_WP_Main_Crossbow_001_WoodWhite", name: "攻速加成", info: "全体攻击速度+10%", field: [7006] },
|
||||
{ uuid: 8707, card_lv: 1, icon: "Chest-FA_Chest_014_Gray", name: "生命加成", info: "全体最大生命+10%", field: [7007] },
|
||||
{ uuid: 8708, card_lv: 1, icon: "Wand-FA_WP_Main_Wand_001_GrayGreen", name: "风怒加成", info: "全体风怒概率+10%", field: [7008] },
|
||||
{ uuid: 8709, card_lv: 1, icon: "Spear-FA_WP_Main_Spear_004_WoodBlue", name: "穿刺加成", info: "全体穿刺概率+10%", field: [7009] },
|
||||
{ uuid: 8710, card_lv: 1, icon: "Shield-FA_WP_Sub_Shield_007_GoldGray", name: "金币收益", info: "每回合金币收益+1", field: [7010] },
|
||||
{ uuid: 8711, card_lv: 1, icon: "Staff-FA_WP_Main_Staff_003_WoodSkyblue", name: "出售强化", info: "卖出英雄金币+1", field: [7011] },
|
||||
{ uuid: 8701, card_lv: 1, icon: "Helmet-FA_Helmet_021_Gray", name: "战后恢复", info: "战斗结束生命回复量+10%", field: [{ uuid: 7001, value: 0.1 }] },
|
||||
{ uuid: 8702, card_lv: 1, icon: "Sword-FA_WP_Main_Sword_003_GrayGold", name: "攻击加成", info: "全体攻击力+10%", field: [{ uuid: 7002, value: 0.1 }] },
|
||||
{ uuid: 8703, card_lv: 1, icon: "Blunt-FA_WP_Main_Blunt_002_Wood", name: "击晕加成", info: "全体击晕概率+10%", field: [{ uuid: 7003, value: 0.1 }] },
|
||||
{ uuid: 8704, card_lv: 1, icon: "Axe-FA_WP_Main_Axe_001_WoodGray", name: "暴击加成", info: "全体暴击率+10%", field: [{ uuid: 7004, value: 0.1 }] },
|
||||
{ uuid: 8705, card_lv: 1, icon: "Bow-FA_WP_Main_Bow_003_WoodGreen", name: "暴伤加成", info: "全体暴击伤害+20%", field: [{ uuid: 7005, value: 0.2 }] },
|
||||
{ uuid: 8706, card_lv: 1, icon: "Crossbow-FA_WP_Main_Crossbow_001_WoodWhite", name: "攻速加成", info: "全体攻击速度+10%", field: [{ uuid: 7006, value: 0.1 }] },
|
||||
{ uuid: 8707, card_lv: 1, icon: "Chest-FA_Chest_014_Gray", name: "生命加成", info: "全体最大生命+10%", field: [{ uuid: 7007, value: 0.1 }] },
|
||||
{ uuid: 8708, card_lv: 1, icon: "Wand-FA_WP_Main_Wand_001_GrayGreen", name: "风怒加成", info: "全体风怒概率+10%", field: [{ uuid: 7008, value: 0.1 }] },
|
||||
{ uuid: 8709, card_lv: 1, icon: "Spear-FA_WP_Main_Spear_004_WoodBlue", name: "穿刺加成", info: "全体穿刺概率+10%", field: [{ uuid: 7009, value: 0.1 }] },
|
||||
{ uuid: 8712, card_lv: 1, icon: "Spear-FA_WP_Main_Spear_004_WoodBlue", name: "破甲加成", info: "全体穿透后伤害+10%", field: [{ uuid: 7020, value: 0.1 }] },
|
||||
{ uuid: 8710, card_lv: 1, icon: "Shield-FA_WP_Sub_Shield_007_GoldGray", name: "金币收益", info: "每回合金币收益+1", field: [{ uuid: 7010, value: 1 }] },
|
||||
{ uuid: 8711, card_lv: 1, icon: "Staff-FA_WP_Main_Staff_003_WoodSkyblue", name: "出售强化", info: "卖出英雄金币+1", field: [{ uuid: 7011, value: 1 }] },
|
||||
// 多词条装备
|
||||
{ uuid: 8720, card_lv: 1, icon: "Shield-FA_WP_Sub_Shield_007_GoldGray", name: "勇者护符", info: "全体攻击力+10%, 暴击率+10%, 暴击伤害+20%", field: [7002, 7004, 7005] },
|
||||
{ uuid: 8720, card_lv: 1, icon: "Shield-FA_WP_Sub_Shield_007_GoldGray", name: "勇者护符", info: "全体攻击力+10%, 暴击率+10%, 暴击伤害+20%", field: [{ uuid: 7002, value: 0.1 }, { uuid: 7004, value: 0.1 }, { uuid: 7005, value: 0.2 }] },
|
||||
|
||||
// ==================== card_lv 2 档(强度 ×2) ====================
|
||||
{ uuid: 8751, card_lv: 2, icon: "Helmet-FA_Helmet_022_DarkBlue", name: "战后恢复", info: "战斗结束生命回复量+20%", field: [7201] },
|
||||
{ uuid: 8752, card_lv: 2, icon: "Sword-FA_WP_Main_Sword_007_Blue", name: "攻击加成", info: "全体攻击力+20%", field: [7202] },
|
||||
{ uuid: 8753, card_lv: 2, icon: "Blunt-FA_WP_Main_Blunt_004_Gray", name: "击晕加成", info: "全体击晕概率+20%", field: [7203] },
|
||||
{ uuid: 8754, card_lv: 2, icon: "Axe-FA_WP_Main_Axe_006_WoodBronze", name: "暴击加成", info: "全体暴击率+20%", field: [7204] },
|
||||
{ uuid: 8755, card_lv: 2, icon: "Bow-FA_WP_Main_Bow_010_Blue", name: "暴伤加成", info: "全体暴击伤害+40%", field: [7205] },
|
||||
{ uuid: 8756, card_lv: 2, icon: "Crossbow-FA_WP_Main_Crossbow_004_Gray", name: "攻速加成", info: "全体攻击速度+20%", field: [7206] },
|
||||
{ uuid: 8757, card_lv: 2, icon: "Chest-FA_Chest_014_BlueGold", name: "生命加成", info: "全体最大生命+20%", field: [7207] },
|
||||
{ uuid: 8758, card_lv: 2, icon: "Wand-FA_WP_Main_Wand_003_BlueWood", name: "风怒加成", info: "全体风怒概率+20%", field: [7208] },
|
||||
{ uuid: 8759, card_lv: 2, icon: "Spear-FA_WP_Main_Spear_007_GraySkyblue", name: "穿刺加成", info: "全体穿刺概率+20%", field: [7209] },
|
||||
{ uuid: 8760, card_lv: 2, icon: "Shield-FA_WP_Sub_Shield_008_BlueGold", name: "金币收益", info: "每回合金币收益+2", field: [7210] },
|
||||
{ uuid: 8761, card_lv: 2, icon: "Shield-FA_WP_Sub_Shield_008_BlueGold", name: "购买优惠", info: "购买卡牌费用-2金币", field: [7212] },
|
||||
{ uuid: 8762, card_lv: 2, icon: "Staff-FA_WP_Main_Staff_007_WoodGray", name: "刷新优惠", info: "刷新卡牌费用-1金币", field: [7213] },
|
||||
{ uuid: 8751, card_lv: 2, icon: "Helmet-FA_Helmet_022_DarkBlue", name: "战后恢复", info: "战斗结束生命回复量+20%", field: [{ uuid: 7001, value: 0.2 }] },
|
||||
{ uuid: 8752, card_lv: 2, icon: "Sword-FA_WP_Main_Sword_007_Blue", name: "攻击加成", info: "全体攻击力+20%", field: [{ uuid: 7002, value: 0.2 }] },
|
||||
{ uuid: 8753, card_lv: 2, icon: "Blunt-FA_WP_Main_Blunt_004_Gray", name: "击晕加成", info: "全体击晕概率+20%", field: [{ uuid: 7003, value: 0.2 }] },
|
||||
{ uuid: 8754, card_lv: 2, icon: "Axe-FA_WP_Main_Axe_006_WoodBronze", name: "暴击加成", info: "全体暴击率+20%", field: [{ uuid: 7004, value: 0.2 }] },
|
||||
{ uuid: 8755, card_lv: 2, icon: "Bow-FA_WP_Main_Bow_010_Blue", name: "暴伤加成", info: "全体暴击伤害+40%", field: [{ uuid: 7005, value: 0.4 }] },
|
||||
{ uuid: 8756, card_lv: 2, icon: "Crossbow-FA_WP_Main_Crossbow_004_Gray", name: "攻速加成", info: "全体攻击速度+20%", field: [{ uuid: 7006, value: 0.2 }] },
|
||||
{ uuid: 8757, card_lv: 2, icon: "Chest-FA_Chest_014_BlueGold", name: "生命加成", info: "全体最大生命+20%", field: [{ uuid: 7007, value: 0.2 }] },
|
||||
{ uuid: 8758, card_lv: 2, icon: "Wand-FA_WP_Main_Wand_003_BlueWood", name: "风怒加成", info: "全体风怒概率+20%", field: [{ uuid: 7008, value: 0.2 }] },
|
||||
{ uuid: 8759, card_lv: 2, icon: "Spear-FA_WP_Main_Spear_007_GraySkyblue", name: "穿刺加成", info: "全体穿刺概率+20%", field: [{ uuid: 7009, value: 0.2 }] },
|
||||
{ uuid: 8763, card_lv: 2, icon: "Spear-FA_WP_Main_Spear_007_GraySkyblue", name: "破甲加成", info: "全体穿透后伤害+20%", field: [{ uuid: 7020, value: 0.2 }] },
|
||||
{ uuid: 8760, card_lv: 2, icon: "Shield-FA_WP_Sub_Shield_008_BlueGold", name: "金币收益", info: "每回合金币收益+2", field: [{ uuid: 7010, value: 2 }] },
|
||||
{ uuid: 8761, card_lv: 2, icon: "Shield-FA_WP_Sub_Shield_008_BlueGold", name: "购买优惠", info: "购买卡牌费用-2金币", field: [{ uuid: 7012, value: 2 }] },
|
||||
{ uuid: 8762, card_lv: 2, icon: "Staff-FA_WP_Main_Staff_007_WoodGray", name: "刷新优惠", info: "刷新卡牌费用-1金币", field: [{ uuid: 7013, value: 1 }] },
|
||||
// 多词条装备
|
||||
{ uuid: 8770, card_lv: 2, icon: "Chest-FA_Chest_014_BlueGold", name: "守卫护甲", info: "全体最大生命+20%, 战后生命回复+20%, 攻速+20%", field: [7207, 7201, 7206] },
|
||||
{ uuid: 8770, card_lv: 2, icon: "Chest-FA_Chest_014_BlueGold", name: "守卫护甲", info: "全体最大生命+20%, 战后生命回复+20%, 攻速+20%", field: [{ uuid: 7007, value: 0.2 }, { uuid: 7001, value: 0.2 }, { uuid: 7006, value: 0.2 }] },
|
||||
|
||||
// ==================== card_lv 3 档(强度 ×3) ====================
|
||||
{ uuid: 8801, card_lv: 3, icon: "Helmet-FA_Helmet_021_RedGold", name: "战后恢复", info: "战斗结束生命回复量+30%", field: [7401] },
|
||||
{ uuid: 8802, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "攻击加成", info: "全体攻击力+30%", field: [7402] },
|
||||
{ uuid: 8803, card_lv: 3, icon: "Blunt-FA_WP_Main_Blunt_006_OrangeGray", name: "击晕加成", info: "全体击晕概率+30%", field: [7403] },
|
||||
{ uuid: 8804, card_lv: 3, icon: "Axe-FA_WP_Main_Axe_012_WoodGold", name: "暴击加成", info: "全体暴击率+30%", field: [7404] },
|
||||
{ uuid: 8805, card_lv: 3, icon: "Bow-FA_WP_Main_Bow_010_Gold", name: "暴伤加成", info: "全体暴击伤害+60%", field: [7405] },
|
||||
{ uuid: 8806, card_lv: 3, icon: "Crossbow-FA_WP_Main_Crossbow_005_WoodGold", name: "攻速加成", info: "全体攻击速度+30%", field: [7406] },
|
||||
{ uuid: 8807, card_lv: 3, icon: "Chest-FA_Chest_015_RedGold", name: "生命加成", info: "全体最大生命+30%", field: [7407] },
|
||||
{ uuid: 8808, card_lv: 3, icon: "Wand-FA_WP_Main_Wand_004_WoodGold", name: "风怒加成", info: "全体风怒概率+30%", field: [7408] },
|
||||
{ uuid: 8809, card_lv: 3, icon: "Spear-FA_WP_Main_Spear_008_GrayGold", name: "穿刺加成", info: "全体穿刺概率+30%", field: [7409] },
|
||||
{ uuid: 8810, card_lv: 3, icon: "Shield-FA_WP_Sub_Shield_009_RedGold", name: "金币收益", info: "每回合金币收益+3", field: [7410] },
|
||||
{ uuid: 8801, card_lv: 3, icon: "Helmet-FA_Helmet_021_RedGold", name: "战后恢复", info: "战斗结束生命回复量+30%", field: [{ uuid: 7001, value: 0.3 }] },
|
||||
{ uuid: 8802, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "攻击加成", info: "全体攻击力+30%", field: [{ uuid: 7002, value: 0.3 }] },
|
||||
{ uuid: 8803, card_lv: 3, icon: "Blunt-FA_WP_Main_Blunt_006_OrangeGray", name: "击晕加成", info: "全体击晕概率+30%", field: [{ uuid: 7003, value: 0.3 }] },
|
||||
{ uuid: 8804, card_lv: 3, icon: "Axe-FA_WP_Main_Axe_012_WoodGold", name: "暴击加成", info: "全体暴击率+30%", field: [{ uuid: 7004, value: 0.3 }] },
|
||||
{ uuid: 8805, card_lv: 3, icon: "Bow-FA_WP_Main_Bow_010_Gold", name: "暴伤加成", info: "全体暴击伤害+60%", field: [{ uuid: 7005, value: 0.6 }] },
|
||||
{ uuid: 8806, card_lv: 3, icon: "Crossbow-FA_WP_Main_Crossbow_005_WoodGold", name: "攻速加成", info: "全体攻击速度+30%", field: [{ uuid: 7006, value: 0.3 }] },
|
||||
{ uuid: 8807, card_lv: 3, icon: "Chest-FA_Chest_015_RedGold", name: "生命加成", info: "全体最大生命+30%", field: [{ uuid: 7007, value: 0.3 }] },
|
||||
{ uuid: 8808, card_lv: 3, icon: "Wand-FA_WP_Main_Wand_004_WoodGold", name: "风怒加成", info: "全体风怒概率+30%", field: [{ uuid: 7008, value: 0.3 }] },
|
||||
{ uuid: 8809, card_lv: 3, icon: "Spear-FA_WP_Main_Spear_008_GrayGold", name: "穿刺加成", info: "全体穿刺概率+30%", field: [{ uuid: 7009, value: 0.3 }] },
|
||||
{ uuid: 8817, card_lv: 3, icon: "Spear-FA_WP_Main_Spear_008_GrayGold", name: "破甲加成", info: "全体穿透后伤害+30%", field: [{ uuid: 7020, value: 0.3 }] },
|
||||
{ uuid: 8810, card_lv: 3, icon: "Shield-FA_WP_Sub_Shield_009_RedGold", name: "金币收益", info: "每回合金币收益+3", field: [{ uuid: 7010, value: 3 }] },
|
||||
// 触发次数强化
|
||||
{ uuid: 8811, card_lv: 3, icon: "Wand-FA_WP_Main_Wand_004_WoodGold", name: "召唤强化", info: "召唤触发技能次数+1", field: [7014] },
|
||||
{ uuid: 8812, card_lv: 3, icon: "Staff-FA_WP_Main_Staff_005_WoodGold", name: "死亡强化", info: "死亡触发技能次数+1", field: [7015] },
|
||||
{ uuid: 8813, card_lv: 3, icon: "Spear-FA_WP_Main_Spear_008_GrayGold", name: "开场强化", info: "战斗开始触发技能次数+1", field: [7016] },
|
||||
{ uuid: 8814, card_lv: 3, icon: "Blunt-FA_WP_Main_Blunt_006_OrangeGray", name: "结束强化", info: "战斗结束触发技能次数+1", field: [7017] },
|
||||
{ uuid: 8815, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "攻击强化", info: "攻击触发技能次数+1", field: [7018] },
|
||||
{ uuid: 8816, card_lv: 3, icon: "Shield-FA_WP_Sub_Shield_009_RedGold", name: "受击强化", info: "被攻击触发技能次数+1", field: [7019] },
|
||||
{ uuid: 8811, card_lv: 3, icon: "Wand-FA_WP_Main_Wand_004_WoodGold", name: "召唤强化", info: "召唤触发技能次数+1", field: [{ uuid: 7014, value: 1 }] },
|
||||
{ uuid: 8812, card_lv: 3, icon: "Staff-FA_WP_Main_Staff_005_WoodGold", name: "死亡强化", info: "死亡触发技能次数+1", field: [{ uuid: 7015, value: 1 }] },
|
||||
{ uuid: 8813, card_lv: 3, icon: "Spear-FA_WP_Main_Spear_008_GrayGold", name: "开场强化", info: "战斗开始触发技能次数+1", field: [{ uuid: 7016, value: 1 }] },
|
||||
{ uuid: 8815, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "攻击强化", info: "攻击触发技能次数+1", field: [{ uuid: 7018, value: 1 }] },
|
||||
{ uuid: 8816, card_lv: 3, icon: "Shield-FA_WP_Sub_Shield_009_RedGold", name: "受击强化", info: "被攻击触发技能次数+1", field: [{ uuid: 7019, value: 1 }] },
|
||||
// 多词条装备
|
||||
{ uuid: 8820, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "神圣之刃", info: "全体攻击力+30%, 暴击率+30%, 暴击伤害+60%, 攻速+30%", field: [7402, 7404, 7405, 7406] },
|
||||
{ uuid: 8820, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "神圣之刃", info: "全体攻击力+30%, 暴击率+30%, 暴击伤害+60%, 攻速+30%", field: [{ uuid: 7002, value: 0.3 }, { uuid: 7004, value: 0.3 }, { uuid: 7005, value: 0.6 }, { uuid: 7006, value: 0.3 }] },
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,6 @@ export enum GameEvent {
|
||||
FightStart = "FightStart",
|
||||
FightPause = "FightPause",
|
||||
FightResume = "FightResume",
|
||||
FightEnd = "FightEnd",
|
||||
MissionEnd = "MissionEnd",
|
||||
MissionComplete = "MissionComplete",//战斗结算完成
|
||||
HeroSkillSelect = "HeroSkillSelect",
|
||||
@@ -88,5 +87,14 @@ export enum GameEvent {
|
||||
UseEquipCard = "UseEquipCard", // 装备卡购买使用事件
|
||||
RemoveEquipBox = "RemoveEquipBox", // 装备盒销毁事件
|
||||
HeroBoxEmptyClick = "HeroBoxEmptyClick", // 英雄面板空槽位点击事件(展示英雄卡池)
|
||||
BossWarning = "BossWarning", // Boss 回合战斗开始预警(payload: { wave, eta },eta 为 Boss 预计进场秒数)
|
||||
/** 回合清屏(场上+待刷怪全灭),payload: { wave, clearTime, allAlive, fastClear } */
|
||||
WaveClear = "WaveClear",
|
||||
/** Boss 实体刷出瞬间(payload: { pos },供登场震屏/音效定位) */
|
||||
BossSpawn = "BossSpawn",
|
||||
/** 金币飞行表现(payload: { worldPos, gold, isBoss },账务已即时结算,纯视觉事件) */
|
||||
CoinFly = "CoinFly",
|
||||
/** 连杀达阈值(payload: { count, tier },tier 0/1/2 对应 5/10/20 连杀) */
|
||||
ComboReach = "ComboReach",
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ export enum BoxSet {
|
||||
MONSTER = 2,
|
||||
HERO = 4,
|
||||
//地图边界
|
||||
LETF_END = -360,
|
||||
RIGHT_END = 360,
|
||||
LETF_END = -320,
|
||||
RIGHT_END = 320,
|
||||
//游戏地平线
|
||||
GAME_LINE = 160,
|
||||
GAME_LINE = 10,
|
||||
/** 技能/装备各自独立的上限 */
|
||||
BOX_MAX = 6,
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export enum FightSet {
|
||||
CRIT_DAMAGE = 50,//暴击伤害
|
||||
MORE_RC = 10,//更多次数 广告获取的次数
|
||||
HEARTPOS = -320,//基地位置
|
||||
HERO_MAX_NUM = 3,//英雄最大数量
|
||||
HERO_MAX_NUM = 10,//英雄最大数量
|
||||
/** 英雄可升级到的最高等级(通过升级卡提升) */
|
||||
HERO_MAX_LV = 6,
|
||||
/** 英雄技能等级上限(无限升级下技能 5 级封顶,超出等级只长属性) */
|
||||
@@ -37,17 +37,19 @@ export enum FightSet {
|
||||
/** 英雄属性随等级成长的倍率(ap/hp = base * MULTIPLIER^(lv-1)) */
|
||||
HERO_LV_MULTIPLIER = 2,
|
||||
// BACK_RANG=30,//后退范围
|
||||
BACK_RANG = 30,//后退范围
|
||||
BACK_RANG = 20,//后退范围
|
||||
FiIGHT_TIME = 30,//战斗时间
|
||||
// BACK_CHANCE=40,//击退概率
|
||||
FROST_TIME = 3,//冰冻时间
|
||||
STUN_TIME = 2,//击晕时间
|
||||
SKILL_CAST_DELAY = 0.15,
|
||||
CSKILL_START_X = -340,
|
||||
CSKILL_START_Y = 30,
|
||||
/** 护盾数值上限(数值减免型:1 点护盾吸收 1 点伤害) */
|
||||
SHIELD_MAX = 5,
|
||||
WAVE_HEAL_RATE = 0.5, // 回合结束时所有英雄恢复最大生命值的比例
|
||||
WAVE_HEAL_RATE = 0.4, // 回合结束时所有英雄恢复最大生命值的比例(与 DDA 放水兜底分层:回血保节奏,放水防崩盘)
|
||||
PUNCTURE_DOWN = 50,
|
||||
/** 穿透伤害衰减系数:单体技能命中第2个及以后目标时按此比例折算伤害 */
|
||||
PUNCTURE_DMG_RATIO = 0.5,
|
||||
REFRESH_COST = 2,
|
||||
BASE_COST = 5,
|
||||
INIT_COIN = 7, // 初始金币数
|
||||
|
||||
@@ -31,11 +31,12 @@ export enum Attrs {
|
||||
freeze_res = "freeze_res", // 冰冻抗性
|
||||
stun_chance = "stun_chance", // 击晕概率
|
||||
stun_res = "stun_res", // 击晕抗性
|
||||
knockback_chance = "knockback_chance", // 击退概率
|
||||
knockback_chance = "knockback_chance", // 击退强化(原击退概率,现转换为击退距离增量)
|
||||
knockback_distance = "knockback_distance", // 击退距离强化
|
||||
knockback_res = "knockback_res", // 击退抗性
|
||||
invincible_time = "invincible_time",// 无敌时间
|
||||
puncture_chance = "puncture_chance", // 穿透概率
|
||||
puncture_dmg_bonus = "puncture_dmg_bonus", // 穿透后伤害增量(百分点;最终穿透系数 = PUNCTURE_DMG_RATIO * (1 + bonus/100))
|
||||
wfuny = "wfuny", // 风怒
|
||||
}
|
||||
|
||||
|
||||
143
assets/script/game/common/config/HeroDesignSpec.md
Normal file
143
assets/script/game/common/config/HeroDesignSpec.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 英雄设定重构设计方案
|
||||
|
||||
> 本文档是 `heroSet.ts` 英雄配置的硬指标规范,所有新增/重构英雄必须遵循。
|
||||
|
||||
## 背景
|
||||
|
||||
对 `heroSet.ts` 中的英雄配置进行模板化重构。5011(铁壁战士)作为标杆保留并改编号为 5001,其余英雄全部推翻重来。
|
||||
|
||||
## 编号体系
|
||||
|
||||
| 号段 | card_lv | 说明 |
|
||||
|------|---------|------|
|
||||
| 5001-5099 | 1 | 绿卡英雄池 |
|
||||
| 5101-5199 | 2 | 蓝卡英雄池 |
|
||||
| 5201-5299 | 3 | 紫卡英雄池 |
|
||||
| 5301-5399 | 4 | 红卡英雄池 |
|
||||
| 5401-5499 | 5 | 金卡英雄池 |
|
||||
|
||||
- 不按触发类型分组编号,按讨论顺序依次分配
|
||||
- 现有英雄只保留 5001(原 5011 铁壁战士),其余全部移除
|
||||
|
||||
## 规则 1:触发节奏模板
|
||||
|
||||
所有触发技能统一 3 档成长:**lv1 → s_lv1, lv3 → s_lv2, lv5 → s_lv3**
|
||||
|
||||
| 触发类型 | t_num 节奏 | 说明 |
|
||||
|----------|-----------|------|
|
||||
| atked(受击) | lv1/lv3 恒 5 次,lv5 降为 4 次 | 满级质变 |
|
||||
| atking(攻击) | lv1/lv3 恒 5 次,lv5 降为 4 次 | 满级质变 |
|
||||
| dead(死亡) | 恒 1 次 | 成长靠数值 |
|
||||
| fstart(回合开始) | 恒 1 次 | 成长靠数值 |
|
||||
| field(光环) | 无 t_num | lv1 基础档(7xxx),lv3 中档(72xxx),lv5 高档(74xxx) |
|
||||
| revive(复活) | 恒 r_num=1 | upr 50% → 60% → 70% |
|
||||
|
||||
**核心原则:lv5 通过降 t_num(或高档数值)实现大幅提升。**
|
||||
|
||||
### field 光环 uuid 编号规则
|
||||
|
||||
| 档位 | uuid 格式 | 示例(攻击光环) |
|
||||
|------|----------|-----------------|
|
||||
| lv1 基础档 | 7xxx | 7002 |
|
||||
| lv3 中档 | 72xxx | 7202 |
|
||||
| lv5 高档 | 74xxx | 7402 |
|
||||
|
||||
## 规则 2:品质-档位标准
|
||||
|
||||
**card_lv = 基础 1 + 能力加成**
|
||||
|
||||
| 能力项 | 加成 | 说明 |
|
||||
|--------|------|------|
|
||||
| 基础触发技能(1个,单体) | +0 | card_lv=1 |
|
||||
| 更高数值 | +1 | card_lv=2 |
|
||||
| ap/hp 成长 | +1 | card_lv=2 |
|
||||
| 范围扩大(单体→全体) | +2 | card_lv=3 |
|
||||
| 双技能(+revive/光环) | +4 | card_lv=5 |
|
||||
|
||||
**组合示例:**
|
||||
- 1 + 0 = card_lv 1(基础)
|
||||
- 1 + 1 = card_lv 2(数值强化 或 ap/hp 成长)
|
||||
- 1 + 2 = card_lv 3(范围扩大)
|
||||
- 1 + 1 + 2 = card_lv 4(数值 + 范围)
|
||||
- 1 + 4 = card_lv 5(双技能)
|
||||
|
||||
**双技能定义**:指英雄同时拥有**两个不同的触发类型字段**(如 atked + revive,或 dead + field)。同一触发类型下的 TriggerGrouped 挂多个 s_uuid 属于主触发类型内的能力组合,不算"双技能"。
|
||||
|
||||
**原则:每个英雄单独讨论,根据能力组合定 card_lv,不死板规定。**
|
||||
|
||||
## 规则 3:命名规则
|
||||
|
||||
**公式:能力关键词 + 职业后缀**
|
||||
|
||||
### 职业后缀词库
|
||||
|
||||
| 职业 | 后缀选项 |
|
||||
|------|---------|
|
||||
| 战士/坦克 | 战士、勇士、斗士、卫士、守护者、壁垒、磐石 |
|
||||
| 刺客 | 刺客、杀手、影刃、猎手、潜行者 |
|
||||
| 弓手 | 弓手、射手、神射、鹰眼、游侠 |
|
||||
| 法师 | 法师、先知、术士、贤者、祭司 |
|
||||
| 辅助 | 牧师、祭司、圣言、吟游诗人 |
|
||||
|
||||
### 能力关键词词库
|
||||
|
||||
| 效果 | 关键词 |
|
||||
|------|--------|
|
||||
| 护盾 | 铁壁、壁垒、磐石、守护 |
|
||||
| 回血 | 不死、再生、血魔、永生 |
|
||||
| 攻击提升 | 狂战、怒血、嗜血、破晓 |
|
||||
| 生命提升 | 不朽、钢骨、生命 |
|
||||
| 暴击 | 致命、绝影、精准 |
|
||||
| 暴伤 | 破灭、毁灭 |
|
||||
| 击晕 | 震撼、威慑 |
|
||||
| 治疗 | 治愈、圣言、祈福 |
|
||||
| 复活 | 不灭、凤凰、涅槃 |
|
||||
| 攻速 | 风怒、迅捷 |
|
||||
| 光环 | 战歌、光环、领域 |
|
||||
| 穿透伤害 | 破甲、贯甲、穿心 |
|
||||
|
||||
### 预设命名示例
|
||||
|
||||
| 触发 | 能力 | 预设名字 |
|
||||
|------|------|---------|
|
||||
| atked | 护盾 | 铁壁卫士、磐石勇士、壁垒守护者 |
|
||||
| atked | 回血 | 不死战士、再生斗士、血魔勇士 |
|
||||
| atked | 攻击 | 怒血狂战、嗜血斗士、破晓勇士 |
|
||||
| atked | 生命 | 钢骨卫士、不朽战士、生命守护者 |
|
||||
| atking | 自身攻击 | 影袭刺客、嗜血杀手、暗夜猎手 |
|
||||
| atking | 自身暴击 | 致命刺客、绝影杀手、精准猎手 |
|
||||
| atking | 队友攻击 | 援护射手、战歌弓手、鼓舞游侠 |
|
||||
| atking | 治疗 | 治愈牧师、圣言祭司、祈福诗人 |
|
||||
| dead | 护盾 | 殉道卫士、守护英魂、牺牲壁垒 |
|
||||
| dead | 攻击/生命 | 遗志将军、英魂战士、传承斗士 |
|
||||
| dead | 暴击/暴伤 | 亡语刺客、诅咒杀手、破灭猎手 |
|
||||
| fstart | 攻击 | 破晓先知、战吼法师、晨光祭司 |
|
||||
| fstart | 护盾 | 圣光祭司、守护贤者、壁垒法师 |
|
||||
| field | 攻速 | 战歌法师、风怒先知、迅捷贤者 |
|
||||
| field | 穿透伤害 | 破甲先知、贯甲贤者、穿心祭司 |
|
||||
| revive | 复活 | 不灭战魂、凤凰祭司、涅槃贤者 |
|
||||
|
||||
## 规则 4:info 描述
|
||||
|
||||
- **info 字段清空**,或作为内部注释使用
|
||||
- 玩家看到的技能信息由系统根据触发技能配置自动生成(取满级数据)
|
||||
|
||||
## 当前英雄池
|
||||
|
||||
| 编号 | 名称 | card_lv | 触发类型 | 能力 | 状态 |
|
||||
|------|------|---------|---------|------|------|
|
||||
| 5001 | 铁壁战士 | 1 | atked | 护盾 | 保留(原 5011) |
|
||||
| 5106 | 破甲射手 | 2 | atking | 穿透伤害(计时buff,技能 6512 + Buff 7022) | 已创建 |
|
||||
| 5002+ | 待定 | 待定 | 待定 | 待定 | 待创建 |
|
||||
|
||||
## 重构代码变更清单
|
||||
|
||||
重构英雄时需同步修改以下位置:
|
||||
1. `heroSet.ts` — `calcHeroPowerNormalized` 的 `baseHeroId` 默认值从 5011 改为 5001
|
||||
2. `heroSet.ts` — `HeroList` 数组更新为新 uuid 列表
|
||||
3. `heroSet.ts` — `HeroInfo` 对象全量替换(移除所有 5012-5071,含注释代码)
|
||||
4. `CardSet.ts` — 自动从 HeroList 生成卡池,需验证
|
||||
|
||||
## 下一步
|
||||
|
||||
逐个讨论并创建新英雄,从 5002 开始。
|
||||
@@ -2,7 +2,7 @@
|
||||
"ver": "1.0.1",
|
||||
"importer": "text",
|
||||
"imported": true,
|
||||
"uuid": "83563f4f-dcd3-4a6f-a2d8-735a643f593e",
|
||||
"uuid": "defa93a3-1e05-4fad-b94c-b12ed9651c9a",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
@@ -1,34 +1,34 @@
|
||||
/**
|
||||
* @file HeroSkillDesc.ts
|
||||
* @description 英雄技能/能力描述的【唯一文字表达标准层】
|
||||
*
|
||||
* 设计目标:
|
||||
* heroSet / SkillSet 中的 info / infos 仅作"基础备注",不再承担面板展示。
|
||||
* 所有功能性描述(触发技能、驻场光环、复活、等级额外属性加成)统一由本模块
|
||||
* 依据结构化配置动态拼出,数值永远来自 mergeSkillParams 合并后的实时配置,
|
||||
* 杜绝"改数值忘改文案"的漂移。
|
||||
*
|
||||
* 输出(双格式,供 UI 按需取用):
|
||||
* - buildSkillDesc(source) → 纯文本(Label 用),数值无颜色
|
||||
* - buildSkillDescRich(source) → 富文本(RichText 用,BBCode),技能名蓝色、数值绿色高亮
|
||||
* 两者共用同一套结构化行构建逻辑,仅渲染阶段区分,保证纯/富文本内容严格一致。
|
||||
*
|
||||
* 通用性(纯配置驱动):
|
||||
* 1. 触发框架:按触发类型遍历(SkillSet + overrides,不动)
|
||||
* 2. 效果文案:查 SkillDescSet 结构化模板渲染
|
||||
* 3. 目标/属性名:由本模块统一语义化(target/hit_count、ATTR_NAME)
|
||||
*
|
||||
* 数据源(入参抽象 ISkillDescSource):
|
||||
* heroInfo(静态 1 级配置)与 HeroAttrsComp(运行时应用 evolve 后的当前等级配置)
|
||||
* 字段结构一致,天然都满足。HeroBoxComp 传 model 显示当前等级;卡池/商店传 heroInfo 显示 1 级。
|
||||
*
|
||||
* 依赖:
|
||||
* - heroSet :SkillTriggerName、SkillTriggerType、TriggerGrouped/LvFieldEntry/LvReviveEntry/LvBonusEntry 及 resolver
|
||||
* - SkillSet :SkillSet、mergeSkillParams、SkillOverrides、SkillKind、TGroup
|
||||
* - SkillDescSet:结构化描述模板
|
||||
* - FieldSkillSet(驻场技能)、BuffList(计时 buff 默认值兜底)、Attrs(属性枚举)
|
||||
*/
|
||||
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, getTriggerSkillName, HGrowType, calcGrowBonus } from "./heroSet";
|
||||
/**
|
||||
* @file HeroSkillDesc.ts
|
||||
* @description 英雄技能/能力描述的【唯一文字表达标准层】
|
||||
*
|
||||
* 设计目标:
|
||||
* heroSet / SkillSet 中的 info / infos 仅作"基础备注",不再承担面板展示。
|
||||
* 所有功能性描述(触发技能、驻场光环、复活、等级额外属性加成)统一由本模块
|
||||
* 依据结构化配置动态拼出,数值永远来自 mergeSkillParams 合并后的实时配置,
|
||||
* 杜绝"改数值忘改文案"的漂移。
|
||||
*
|
||||
* 输出(双格式,供 UI 按需取用):
|
||||
* - buildSkillDesc(source) → 纯文本(Label 用),数值无颜色
|
||||
* - buildSkillDescRich(source) → 富文本(RichText 用,BBCode),技能名蓝色、数值绿色高亮
|
||||
* 两者共用同一套结构化行构建逻辑,仅渲染阶段区分,保证纯/富文本内容严格一致。
|
||||
*
|
||||
* 通用性(纯配置驱动):
|
||||
* 1. 触发框架:按触发类型遍历(SkillSet + overrides,不动)
|
||||
* 2. 效果文案:查 SkillDescSet 结构化模板渲染
|
||||
* 3. 目标/属性名:由本模块统一语义化(target/hit_count、ATTR_NAME)
|
||||
*
|
||||
* 数据源(入参抽象 ISkillDescSource):
|
||||
* heroInfo(静态 1 级配置)与 HeroAttrsComp(运行时应用 evolve 后的当前等级配置)
|
||||
* 字段结构一致,天然都满足。HeroBoxComp 传 model 显示当前等级;卡池/商店传 heroInfo 显示 1 级。
|
||||
*
|
||||
* 依赖:
|
||||
* - heroSet :SkillTriggerName、SkillTriggerType、TriggerGrouped/LvFieldEntry/LvReviveEntry/LvBonusEntry 及 resolver
|
||||
* - SkillSet :SkillSet、mergeSkillParams、SkillOverrides、SkillKind、TGroup
|
||||
* - SkillDescSet:结构化描述模板
|
||||
* - FieldSkillSet(驻场技能)、BuffList(计时 buff 默认值兜底)、Attrs(属性枚举)
|
||||
*/
|
||||
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, FieldEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, getTriggerSkillName, HGrowType, calcGrowBonus } from "./heroSet";
|
||||
import { FieldSkillSet, mergeSkillParams, SkillConfig, SkillKind, SkillOverrides, SkillSet, TGroup } from "./SkillSet";
|
||||
import { FieldDescSet, FieldValueFmt, SkillDescSet } from "./SkillDescSet";
|
||||
import { BuffList } from "./BuffSet";
|
||||
@@ -45,7 +45,6 @@ export interface ISkillDescSource {
|
||||
call?: TriggerGrouped;
|
||||
dead?: TriggerGrouped;
|
||||
fstart?: TriggerGrouped;
|
||||
fend?: TriggerGrouped;
|
||||
atking?: TriggerGrouped;
|
||||
atked?: TriggerGrouped;
|
||||
field?: LvFieldEntry[];
|
||||
@@ -85,7 +84,6 @@ const TRIGGER_KEYS: SkillTriggerType[] = [
|
||||
SkillTriggerType.Call,
|
||||
SkillTriggerType.Dead,
|
||||
SkillTriggerType.FStart,
|
||||
SkillTriggerType.FEnd,
|
||||
SkillTriggerType.Atking,
|
||||
SkillTriggerType.Atked,
|
||||
];
|
||||
@@ -99,6 +97,18 @@ const ATTR_NAME: Partial<Record<Attrs, string>> = {
|
||||
/** 富文本数值高亮色(鲜亮绿色,深色面板上高亮醒目) */
|
||||
const RICH_VALUE_COLOR = "#3CE66E";
|
||||
|
||||
/** 数值描边色(深绿,衬托亮绿填充,与技能名橙填充+深褐描边同一设计思路) */
|
||||
const RICH_VALUE_OUTLINE = "#145A32";
|
||||
|
||||
/**
|
||||
* 数值富文本片段:亮绿填充 + 深绿描边。
|
||||
* 高亮数值明度高,深色描边增强边缘对比、提升深色面板上的锐度。
|
||||
* @param text 数值文本(含单位,如 "15%"、"4次")
|
||||
*/
|
||||
function valueRich(text: string | number): string {
|
||||
return `<outline color=${RICH_VALUE_OUTLINE} width=1><color=${RICH_VALUE_COLOR}>${text}</color></outline>`;
|
||||
}
|
||||
|
||||
/** 富文本技能名高亮色(蜜桃橙,暖色系,与面板绿/蓝/红/黄/紫均不冲突) */
|
||||
const RICH_NAME_COLOR = "#F8C471";
|
||||
|
||||
@@ -230,26 +240,26 @@ function buildEffectLine(head: string, skill: SkillConfig): ISkillLine {
|
||||
* @param showName 是否在行中包含「技能名」片段(英雄面板需要,装备商店不需要)
|
||||
* @param nameOverride 技能名覆盖(英雄语境传入 heroSet 语境专属命名,缺省用 FieldSkillSet 通用名)
|
||||
*/
|
||||
function buildFieldLine(head: string, uuid: number, showName: boolean = true, nameOverride?: string): ISkillLine | null {
|
||||
const fs = FieldSkillSet[uuid];
|
||||
function buildFieldLine(head: string, entry: FieldEntry, showName: boolean = true, nameOverride?: string): ISkillLine | null {
|
||||
const fs = FieldSkillSet[entry.uuid];
|
||||
if (!fs) return null;
|
||||
const desc = FieldDescSet[fs.type];
|
||||
const nameSeg = showName ? `「${nameOverride ?? fs.name}」 ` : "";
|
||||
// 未入表类型回退基础备注 info,整行不高亮
|
||||
if (!desc) return { prefix: `${head}${nameSeg}${fs.info}`, value: "", suffix: "" };
|
||||
|
||||
// 数值按格式渲染为高亮片段
|
||||
// 数值按格式渲染为高亮片段(数值取自词条自身 value)
|
||||
let valueText: string;
|
||||
switch (desc.fmt) {
|
||||
case FieldValueFmt.Percent:
|
||||
valueText = `+${Math.round(fs.value * 100)}%`;
|
||||
valueText = `+${Math.round(entry.value * 100)}%`;
|
||||
break;
|
||||
case FieldValueFmt.IntMinus:
|
||||
valueText = `-${fs.value}`;
|
||||
valueText = `-${entry.value}`;
|
||||
break;
|
||||
case FieldValueFmt.Int:
|
||||
default:
|
||||
valueText = `+${fs.value}`;
|
||||
valueText = `+${entry.value}`;
|
||||
break;
|
||||
}
|
||||
return { prefix: `${head}${nameSeg}${desc.verb}`, value: valueText, suffix: "" };
|
||||
@@ -421,8 +431,8 @@ function buildSkillLines(source: ISkillDescSource, showName: boolean = true, sho
|
||||
if (fieldEntries?.length) {
|
||||
const tpl = SkillTriggerName[SkillTriggerType.Field] ?? "光环";
|
||||
if (!showLocked) {
|
||||
for (const uuid of resolveFieldByLv(fieldEntries, lv)) {
|
||||
const line = buildFieldLine(`${tpl}:`, uuid, showName);
|
||||
for (const entry of resolveFieldByLv(fieldEntries, lv)) {
|
||||
const line = buildFieldLine(`${tpl}:`, entry, showName);
|
||||
if (line) lines.push(line);
|
||||
}
|
||||
} else {
|
||||
@@ -432,15 +442,15 @@ function buildSkillLines(source: ISkillDescSource, showName: boolean = true, sho
|
||||
let activeTier: LvFieldEntry | undefined;
|
||||
for (const tier of sorted) { if (tier.lv <= lv) activeTier = tier; }
|
||||
if (activeTier) {
|
||||
for (const uuid of activeTier.uuids) {
|
||||
const line = buildFieldLine(`Lv${activeTier.lv}: ${tpl} `, uuid, showName);
|
||||
for (const entry of activeTier.uuids) {
|
||||
const line = buildFieldLine(`Lv${activeTier.lv}: ${tpl} `, entry, showName);
|
||||
if (line) lines.push(line);
|
||||
}
|
||||
}
|
||||
for (const tier of sorted) {
|
||||
if (tier.lv <= lv) continue;
|
||||
for (const uuid of tier.uuids) {
|
||||
const line = buildFieldLine(`Lv${tier.lv}: ${tpl} `, uuid, showName);
|
||||
for (const entry of tier.uuids) {
|
||||
const line = buildFieldLine(`Lv${tier.lv}: ${tpl} `, entry, showName);
|
||||
if (!line) continue;
|
||||
line.locked = true;
|
||||
lines.push(line);
|
||||
@@ -498,14 +508,14 @@ export function buildSingleSkillRich(skillUuid: number, overrides?: SkillOverrid
|
||||
const skill = mergeSkillParams(base, overrides);
|
||||
// SkillConfig 未定义 cd 字段,冷却时间由外部运行时数据传入;计时模式展示持续时间
|
||||
const dur = skill.buff_duration ?? (skill.timed_buff_id !== undefined ? BuffList[skill.timed_buff_id]?.duration : undefined) ?? 0;
|
||||
const cdTag = dur > 0 ? `<color=${RICH_VALUE_COLOR}>持续:${dur}s</color>` : "";
|
||||
const cdTag = dur > 0 ? valueRich(`持续:${dur}s`) : "";
|
||||
const nameTag = `${skillNameRich(skill.name)} Lv.${lv}`;
|
||||
|
||||
// 复用结构化效果行,head 为空避免重复触发类型前缀
|
||||
const line = buildEffectLine("", skill);
|
||||
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
||||
const effect = line.value
|
||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
|
||||
? `${prefix}${valueRich(line.value)}${line.suffix}`
|
||||
: `${prefix}${line.suffix}`;
|
||||
|
||||
return cdTag ? `${nameTag} ${cdTag}\n${effect}` : `${nameTag}\n${effect}`;
|
||||
@@ -513,19 +523,19 @@ export function buildSingleSkillRich(skillUuid: number, overrides?: SkillOverrid
|
||||
|
||||
/**
|
||||
* 装备/驻场光环详情富文本:展示全部词条,数值绿色高亮。
|
||||
* @param fieldUuids FieldSkillSet 驻场光环 UUID 数组
|
||||
* @param showName 是否包含「技能名」片段(英雄面板 true,装备商店 false)
|
||||
* @returns 多行富文本串,每行一个词条
|
||||
* @param entries 驻场光环词条数组(uuid 指向 FieldSkillSet 底座,value 为生效数值)
|
||||
* @param showName 是否包含「技能名」片段(英雄面板 true,装备商店 false)
|
||||
* @returns 多行富文本串,每行一个词条
|
||||
*/
|
||||
export function buildEquipRich(fieldUuids: number[], showName: boolean = false): string {
|
||||
if (!fieldUuids?.length) return "无词条";
|
||||
export function buildEquipRich(entries: FieldEntry[], showName: boolean = false): string {
|
||||
if (!entries?.length) return "无词条";
|
||||
const lines: string[] = [];
|
||||
for (const uuid of fieldUuids) {
|
||||
const line = buildFieldLine("", uuid, showName);
|
||||
for (const entry of entries) {
|
||||
const line = buildFieldLine("", entry, showName);
|
||||
if (!line) continue;
|
||||
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
||||
const text = line.value
|
||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
|
||||
? `${prefix}${valueRich(line.value)}${line.suffix}`
|
||||
: `${prefix}${line.suffix}`;
|
||||
lines.push(text);
|
||||
}
|
||||
@@ -542,10 +552,11 @@ export function buildEquipRich(fieldUuids: number[], showName: boolean = false):
|
||||
* 数据源为运行时 HeroAttrsComp(lv 取当前英雄等级),逐触发类型逐技能渲染。
|
||||
* 技能等级取 LvTriggerEntry.s_lv,未标注时按档位序号(1/2/3...)兜底。
|
||||
*
|
||||
* @param source 英雄运行时数据源(含 lv 与触发技能分组)
|
||||
* @returns 多行富文本串,\n 分隔
|
||||
* @param source 英雄运行时数据源(含 lv 与触发技能分组)
|
||||
* @param showName 是否展示「技能名」片段(英雄信息弹窗 true,英雄卡池卡片 false)
|
||||
* @returns 多行富文本串,\n 分隔
|
||||
*/
|
||||
export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
||||
export function buildSkillDescMoreRich(source: ISkillDescSource, showName: boolean = true): string {
|
||||
const lv = Math.max(1, source.lv ?? 1);
|
||||
const lines: string[] = [];
|
||||
|
||||
@@ -553,7 +564,7 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
||||
const effectRich = (line: ISkillLine): string => {
|
||||
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
||||
const text = line.value
|
||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
|
||||
? `${prefix}${valueRich(line.value)}${line.suffix}`
|
||||
: `${prefix}${line.suffix}`;
|
||||
return highlightDuration(text);
|
||||
};
|
||||
@@ -584,22 +595,20 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
||||
const curSLv = cur.s_lv ?? (curIdx + 1);
|
||||
// 触发次数直接写为富文本(数字绿色高亮),如 "受伤3次"
|
||||
const curTrig = needCount
|
||||
? `${trigName}<color=${RICH_VALUE_COLOR}>${cur.t_num}</color>次`
|
||||
? `${trigName}${valueRich(cur.t_num)}次`
|
||||
: trigName;
|
||||
// 技能名 + 档位数字后缀(如 "不屈壁垒1"),与光环命名口径一致
|
||||
const curHead = `「${skillName}${curSLv}」: ${curTrig}, `;
|
||||
// 技能名直接展示,不附加档位数字后缀(由 lv 字段控制生效档,玩家无需关注内部档号)
|
||||
const curHead = showName ? `「${skillName}」: ${curTrig}, ` : `${curTrig}, `;
|
||||
lines.push(effectRich(buildEffectLine(curHead, mergeSkillParams(base, cur.overrides))));
|
||||
}
|
||||
}
|
||||
|
||||
// 驻场光环(field):技能名按档位带数字后缀(如 [攻击光环1]/[攻击光环2]),便于玩家识别成长
|
||||
// 驻场光环(field):直接展示技能名,不附加档位数字后缀
|
||||
const fieldEntries = source[SkillTriggerType.Field] as LvFieldEntry[] | undefined;
|
||||
if (fieldEntries?.length) {
|
||||
for (const uuid of resolveFieldByLv(fieldEntries, lv)) {
|
||||
// uuid 百位编码档位:7002 基础(0)=1、7202 +(2)=2、7402 ++(4)=3
|
||||
const tier = Math.floor((uuid % 1000) / 100) / 2 + 1;
|
||||
const baseName = getTriggerSkillName(SkillTriggerType.Field, uuid);
|
||||
const line = buildFieldLine("", uuid, true, `${baseName}${tier}`);
|
||||
for (const entry of resolveFieldByLv(fieldEntries, lv)) {
|
||||
const baseName = getTriggerSkillName(SkillTriggerType.Field, entry.uuid);
|
||||
const line = buildFieldLine("", entry, showName, baseName);
|
||||
if (line) lines.push(effectRich(line));
|
||||
}
|
||||
}
|
||||
@@ -608,7 +617,9 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
||||
const revive = resolveReviveByLv(source[SkillTriggerType.Revive] as LvReviveEntry[] | undefined, lv);
|
||||
if (revive) {
|
||||
const reviveName = getTriggerSkillName(SkillTriggerType.Revive, revive.s_uuid);
|
||||
lines.push(`${skillNameRich(reviveName)}: 每回合拥有<color=${RICH_VALUE_COLOR}>${revive.r_num}</color>次重生机会, 死亡后以<color=${RICH_VALUE_COLOR}>${Math.round(revive.upr * 100)}%</color>血量原地重生`);
|
||||
lines.push(showName
|
||||
? `${skillNameRich(reviveName)}: 每回合拥有${valueRich(revive.r_num)}次重生机会, 死亡后以${valueRich(`${Math.round(revive.upr * 100)}%`)}血量原地重生`
|
||||
: `每回合拥有${valueRich(revive.r_num)}次重生机会, 死亡后以${valueRich(`${Math.round(revive.upr * 100)}%`)}血量原地重生`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
@@ -620,7 +631,7 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
||||
* 持续时间对药水/计时 buff 同样关键,这里补充高亮,保证视觉一致。
|
||||
*/
|
||||
function highlightDuration(text: string): string {
|
||||
return text.replace(DURATION_PATTERN, `持续<color=${RICH_VALUE_COLOR}>$1</color>秒`);
|
||||
return text.replace(DURATION_PATTERN, `持续${valueRich("$1")}秒`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -634,7 +645,7 @@ function renderLineRich(line: ISkillLine): string {
|
||||
// 未来档数值灰色追加在当前高亮数值后("15%/20%/30%"),suffix 仍位于整段数值之后
|
||||
const next = line.nextValues?.length ? `<color=${RICH_LOCKED_COLOR}>/${line.nextValues.join("/")}</color>` : "";
|
||||
return line.value
|
||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${next}${line.suffix}`
|
||||
? `${prefix}${valueRich(line.value)}${next}${line.suffix}`
|
||||
: `${prefix}${next}${line.suffix}`;
|
||||
}
|
||||
|
||||
@@ -646,7 +657,7 @@ function renderLineRich(line: ISkillLine): string {
|
||||
function buildChanceSuffix(skill: SkillConfig): string {
|
||||
const items: string[] = [];
|
||||
const push = (val: number | undefined, label: string) => {
|
||||
if (val && val > 0) items.push(`附加<color=${RICH_VALUE_COLOR}>${val}</color>%${label}`);
|
||||
if (val && val > 0) items.push(`附加${valueRich(val)}%${label}`);
|
||||
};
|
||||
push(skill.crt, "暴击");
|
||||
push(skill.frz, "冰冻");
|
||||
@@ -699,8 +710,8 @@ export function buildCardDescRich(card: CardConfig): string {
|
||||
return rich;
|
||||
}
|
||||
|
||||
const rhythm = `每<color=${RICH_VALUE_COLOR}>${tInv}</color>秒召唤<color=${RICH_VALUE_COLOR}>${(card.overrides?.num ?? 0) + 1}</color>个${skillNameRich(skill.name)}攻击敌人`;
|
||||
const times = `, 共<color=${RICH_VALUE_COLOR}>${tTimes}</color>次`;
|
||||
const rhythm = `每${valueRich(tInv)}秒召唤${valueRich((card.overrides?.num ?? 0) + 1)}个${skillNameRich(skill.name)}攻击敌人`;
|
||||
const times = `, 共${valueRich(tTimes)}次`;
|
||||
return `${rhythm}, ${rich}${times}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const ItemCardData: ItemCardRaw[] = [
|
||||
// ==================== 计时性强化(统一 5s) ====================
|
||||
{ uuid: 9102, skill: 6503, icon: "Sub_Item-FA_WP_Sub_Potion_001_Red", name: "攻击药水", info: "全体友方攻击力提升", cost: 0, weight: 15 },
|
||||
{ uuid: 9103, skill: 6504, icon: "Sub_Item-FA_WP_Sub_Potion_001_Yellow", name: "暴击药水", info: "全体友方暴击率提升", cost: 0, weight: 15 },
|
||||
{ uuid: 9104, skill: 6505, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "击退药水", info: "全体友方击退概率提升", cost: 0, weight: 12 },
|
||||
{ uuid: 9104, skill: 6505, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "击退药水", info: "全体友方击退距离提升", cost: 0, weight: 12 },
|
||||
{ uuid: 9105, skill: 6506, icon: "Sub_Item-FA_WP_Sub_Potion_001_Blue", name: "穿透药水", info: "全体友方穿透概率提升", cost: 0, weight: 12 },
|
||||
{ uuid: 9106, skill: 6507, icon: "Sub_Item-FA_WP_Sub_Potion_001_Red", name: "攻速药水", info: "全体友方攻击速度提升", cost: 0, weight: 15 },
|
||||
{ uuid: 9107, skill: 6508, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "风怒药水", info: "全体友方风怒概率提升", cost: 0, weight: 12 },
|
||||
|
||||
@@ -221,7 +221,7 @@ export const SkillDescSet: Record<number, SkillDescConfig> = {
|
||||
templTimed: "{target}{verb}{value}{unit},持续{dur}秒",
|
||||
},
|
||||
6505: {
|
||||
verb: "击退概率提升", unit: "%", permField: "ap", timedField: "buff_value",
|
||||
verb: "击退距离提升", unit: "", permField: "ap", timedField: "buff_value",
|
||||
templPerm: "{target}{verb}{value}{unit}",
|
||||
templTimed: "{target}{verb}{value}{unit},持续{dur}秒",
|
||||
},
|
||||
@@ -281,13 +281,12 @@ export interface FieldDescConfig {
|
||||
|
||||
/**
|
||||
* 驻场光环描述表,key = FieldSkillType。
|
||||
* 数值永远取 FieldSkillConfig.value 实时渲染,替代手写 info,杜绝漂移。
|
||||
* 数值取配置处词条(FieldEntry.value)实时渲染,替代手写 info,杜绝漂移。
|
||||
*/
|
||||
export const FieldDescSet: Record<number, FieldDescConfig> = {
|
||||
[FieldSkillType.SummonCount]: { verb: "召唤触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.DeadCount]: { verb: "死亡触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.StartCount]: { verb: "战斗开始触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.EndCount]: { verb: "战斗结束触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.WaveGold]: { verb: "每回合金币收益", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.SellGold]: { verb: "卖出英雄金币", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.WaveHeal]: { verb: "战斗结束生命回复量", fmt: FieldValueFmt.Percent },
|
||||
@@ -301,6 +300,7 @@ export const FieldDescSet: Record<number, FieldDescConfig> = {
|
||||
[FieldSkillType.HeroHp]: { verb: "全体最大生命", fmt: FieldValueFmt.Percent },
|
||||
[FieldSkillType.HeroWindFury]: { verb: "全体风怒概率", fmt: FieldValueFmt.Percent },
|
||||
[FieldSkillType.HeroPuncture]: { verb: "全体穿刺概率", fmt: FieldValueFmt.Percent },
|
||||
[FieldSkillType.HeroPunctureDmg]: { verb: "全体穿透后伤害", fmt: FieldValueFmt.Percent },
|
||||
[FieldSkillType.AtkCount]: { verb: "攻击触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.BeAtkCount]: { verb: "被攻击触发技能次数", fmt: FieldValueFmt.Int },
|
||||
};
|
||||
|
||||
@@ -152,7 +152,7 @@ export interface SkillConfig {
|
||||
crt?: number, // 额外暴击率
|
||||
frz?: number, // 额外冰冻概率
|
||||
stun?: number, // 额外击晕概率
|
||||
bck?: number, // 额外击退概率
|
||||
bck?: number, // 额外击退强化(击退距离增量)
|
||||
buff_type?: Attrs, // Buff 类型 (单一职责)
|
||||
timed_buff_id?: number, // 计时性 buff 配置 id(指向 BuffList),存在时走 buffManager 而非永久 add_*
|
||||
buff_value?: number, // 覆写计时 buff 的修饰数值(modifiers.value / tick.damage_or_heal),由药水卡自定义档位
|
||||
@@ -216,37 +216,37 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
6001: {
|
||||
uuid: 6001, name: "飞剑", sp_name: "a1", icon: "Sword-FA_WP_Main_Sword_003_PurpleSilver", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6002: {
|
||||
6002: {
|
||||
uuid: 6002, name: "飞刀", sp_name: "a2", icon: "Sword-FA_WP_Main_Sword_009_GraySilver", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6003: {
|
||||
6003: {
|
||||
uuid: 6003, name: "匕首", sp_name: "a3", icon: "Sword-FA_WP_Main_Sword_019_WoodSilver", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6004: {
|
||||
6004: {
|
||||
uuid: 6004, name: "飞斧", sp_name: "a4", icon: "Axe-FA_WP_Main_Axe_004_WoodGray", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6005: {
|
||||
6005: {
|
||||
uuid: 6005, name: "飞枪", sp_name: "a5", icon: "Spear-FA_WP_Main_Spear_010_MetalBlue", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6006: {
|
||||
uuid: 6006, name: "攻击", sp_name: "atk", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
uuid: 6006, name: "攻击", sp_name: "atk1", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6007: {
|
||||
uuid: 6007, name: "飞棒", sp_name: "b1", icon: "Blunt-FA_WP_Main_Blunt_002_Wood", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6008: {
|
||||
uuid: 6008, name: "射击", sp_name: "aw2", icon: "Stat_RangedAttack", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
@@ -266,32 +266,32 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
6011: {
|
||||
uuid: 6011, name: "飞锤", sp_name: "b2", icon: "Blunt-FA_WP_Main_Blunt_005_Gray", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6012: {
|
||||
uuid: 6012, name: "飞斧", sp_name: "b2", icon: "Axe-FA_WP_Main_Axe_002_WoodGray", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6013: {
|
||||
uuid: 6013, name: "蓝魔球", sp_name: "m1", icon: "Sub_Item-FA_WP_Sub_MagicBall_001_Blue", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6014: {
|
||||
uuid: 6014, name: "紫魔球", sp_name: "m2", icon: " Sub_Item-FA_WP_Sub_MagicBall_001_Purple", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6015: {
|
||||
uuid: 6015, name: "绿药瓶", sp_name: "h1", icon: "Sub_Item-FA_WP_Sub_Potion_001_Green", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6016: {
|
||||
uuid: 6016, name: "红药瓶", sp_name: "h2", icon: "Sub_Item-FA_WP_Sub_Potion_001_Red", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
/*** ======中阶范围攻击技能 ====
|
||||
* 都带有穿刺属性,至少穿透1 个目标 攻击2 个目标, 升级方向是 最多 穿透3 个攻击 4 个目标
|
||||
@@ -303,7 +303,7 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
6101: {
|
||||
uuid: 6101, name: "火球", sp_name: "fb-1", icon: "Stat_FireDamage", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, stun: 0, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害, 一定几率暴击, 高阶技能",
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害, 一定几率暴击, 高阶技能",
|
||||
},
|
||||
//怪物法师统一使用 暗影球
|
||||
6102: {
|
||||
@@ -363,7 +363,7 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
6406: {
|
||||
uuid: 6406, name: "击退强化", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 1, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退概率限时提升, 持续5秒",
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退距离限时提升, 持续5秒",
|
||||
},
|
||||
6407: {
|
||||
uuid: 6407, name: "距推强化", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
@@ -385,6 +385,11 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 1, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.freeze_chance, timed_buff_id: 7021, info: "全体友方冰冻概率限时提升, 持续5秒",
|
||||
},
|
||||
6411: {
|
||||
uuid: 6411, name: "穿透伤害强化", sp_name: "buff_wind", icon: "Stat_Tripleshot", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 10, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.puncture_dmg_bonus, info: "全体友方穿透后伤害提升10%(百分点), 永久生效",
|
||||
},
|
||||
6501: {
|
||||
uuid: 6501, name: "复活", sp_name: "buff_wind", icon: "Stat_HolyDamage", TGroup: TGroup.Self, readyAnm: "up_ap", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 50, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
@@ -411,7 +416,7 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
6505: {
|
||||
uuid: 6505, name: "击退爆发", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退概率提升30%, 持续5秒",
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退距离提升30, 持续5秒",
|
||||
},
|
||||
6506: {
|
||||
uuid: 6506, name: "穿透爆发", sp_name: "buff_wind", icon: "Stat_Tripleshot", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
|
||||
@@ -442,6 +447,11 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
uuid: 6511, name: "冰冻抗性", sp_name: "buff_wind", icon: "Stat_Freeze", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.freeze_res, timed_buff_id: 7019, info: "全体友方冰冻抗性提升50%, 持续5秒",
|
||||
},
|
||||
6512: {
|
||||
uuid: 6512, name: "穿透伤害爆发", sp_name: "buff_wind", icon: "Stat_Tripleshot", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.puncture_dmg_bonus, timed_buff_id: 7022, info: "全体友方穿透后伤害提升30%, 持续5秒",
|
||||
}
|
||||
|
||||
};
|
||||
@@ -450,7 +460,6 @@ export enum FieldSkillType {
|
||||
SummonCount = 1, // 召唤触发技能次数提升
|
||||
DeadCount = 2, // 死亡触发技能次数提升
|
||||
StartCount = 3, // 战斗开始触发技能次数提升
|
||||
EndCount = 4, // 战斗结束触发技能次数提升
|
||||
WaveGold = 5, // 每回合金币收益提升
|
||||
SellGold = 6, // 卖出英雄金币提升
|
||||
WaveHeal = 7, // 战斗结束生命回复量提升
|
||||
@@ -468,6 +477,7 @@ export enum FieldSkillType {
|
||||
HeroPuncture = 18, // 英雄穿刺概率加成
|
||||
AtkCount = 19, // 攻击触发技能次数提升
|
||||
BeAtkCount = 20, // 被攻击触发技能次数提升
|
||||
HeroPunctureDmg = 21, // 英雄穿透后伤害加成(百分点;乘算:PUNCTURE_DMG_RATIO * (1 + bonus/100))
|
||||
}
|
||||
|
||||
export interface FieldSkillConfig {
|
||||
@@ -475,68 +485,36 @@ export interface FieldSkillConfig {
|
||||
name: string;
|
||||
icon: string; // 新增图标字段
|
||||
type: FieldSkillType;
|
||||
value: number; // 提升的数值
|
||||
info: string;
|
||||
info: string; // 描述模板(不含具体数值,数值由配置处携带)
|
||||
}
|
||||
|
||||
/**
|
||||
* 驻场光环底座表:每个 FieldSkillType 仅保留一条基础配置。
|
||||
*
|
||||
* Why: 同一光环因数值不同拆成多个 uuid(7002/7202/7402)会造成大量重复条目。
|
||||
* 合并后数值由配置处(heroSet/EquipSet 的 field 条目)携带,此处只维护
|
||||
* 类型/图标/名称等元数据。所有光环 uuid 统一使用 7001~7020 基础段。
|
||||
*/
|
||||
export const FieldSkillSet: Record<number, FieldSkillConfig> = {
|
||||
7001: { uuid: 7001, name: "战后恢复", icon: "Stat_PotionBoost", type: FieldSkillType.WaveHeal, info: "战斗结束生命回复量提升" },
|
||||
7002: { uuid: 7002, name: "攻击光环", icon: "Stat_Attack_03", type: FieldSkillType.HeroAtk, info: "英雄攻击力提升" },
|
||||
7003: { uuid: 7003, name: "击晕光环", icon: "Stat_Stun_01", type: FieldSkillType.HeroStun, info: "英雄击晕概率提升" },
|
||||
7004: { uuid: 7004, name: "暴击光环", icon: "Stat_CriticalChance_02", type: FieldSkillType.HeroCrit, info: "英雄暴击率提升" },
|
||||
7005: { uuid: 7005, name: "暴伤光环", icon: "Stat_Critical_01", type: FieldSkillType.HeroCritDamage, info: "英雄暴击伤害提升" },
|
||||
7006: { uuid: 7006, name: "攻速光环", icon: "Stat_AttackSpeed_02", type: FieldSkillType.HeroSpeed, info: "英雄攻击速度提升" },
|
||||
7007: { uuid: 7007, name: "生命光环", icon: "Stat_Hp_02", type: FieldSkillType.HeroHp, info: "英雄最大生命提升" },
|
||||
7008: { uuid: 7008, name: "风怒光环", icon: "Stat_CriticalComboChance", type: FieldSkillType.HeroWindFury, info: "英雄风怒概率提升" },
|
||||
7009: { uuid: 7009, name: "穿刺光环", icon: "Stat_Tripleshot", type: FieldSkillType.HeroPuncture, info: "英雄穿刺概率提升" },
|
||||
|
||||
7010: { uuid: 7010, name: "金币收益", icon: "Stat_InventorySlotIncrease", type: FieldSkillType.WaveGold, info: "每回合金币收益提升" },
|
||||
7011: { uuid: 7011, name: "出售强化", icon: "Stat_GoldGainIncrease_01", type: FieldSkillType.SellGold, info: "卖出英雄金币提升" },
|
||||
7012: { uuid: 7012, name: "购买优惠", icon: "Stat_KeyCapacityIncrease", type: FieldSkillType.BuyDiscount, info: "购买卡牌费用减免" },
|
||||
7013: { uuid: 7013, name: "刷新优惠", icon: "Stat_RandomBonus", type: FieldSkillType.RefreshDiscount, info: "刷新卡牌费用减免" },
|
||||
|
||||
7001: { uuid: 7001, name: "战后恢复", icon: "Stat_PotionBoost", type: FieldSkillType.WaveHeal, value: 0.1, info: "战斗结束生命回复量+10%" },
|
||||
7002: { uuid: 7002, name: "攻击光环", icon: "Stat_Attack_03", type: FieldSkillType.HeroAtk, value: 0.1, info: "英雄攻击力+10%" },
|
||||
7003: { uuid: 7003, name: "击晕光环", icon: "Stat_Stun_01", type: FieldSkillType.HeroStun, value: 0.1, info: "英雄击晕概率+10%" },
|
||||
7004: { uuid: 7004, name: "暴击光环", icon: "Stat_CriticalChance_02", type: FieldSkillType.HeroCrit, value: 0.1, info: "英雄暴击率+10%" },
|
||||
7005: { uuid: 7005, name: "暴伤光环", icon: "Stat_Critical_01", type: FieldSkillType.HeroCritDamage, value: 0.2, info: "英雄暴击伤害+20%" },
|
||||
7006: { uuid: 7006, name: "攻速光环", icon: "Stat_AttackSpeed_02", type: FieldSkillType.HeroSpeed, value: 0.1, info: "英雄攻击速度+10%" },
|
||||
7007: { uuid: 7007, name: "生命光环", icon: "Stat_Hp_02", type: FieldSkillType.HeroHp, value: 0.1, info: "英雄最大生命+10%" },
|
||||
7008: { uuid: 7008, name: "风怒光环", icon: "Stat_CriticalComboChance", type: FieldSkillType.HeroWindFury, value: 0.1, info: "英雄风怒概率+10%" },
|
||||
7009: { uuid: 7009, name: "穿刺光环", icon: "Stat_Tripleshot", type: FieldSkillType.HeroPuncture, value: 0.1, info: "英雄穿刺概率+10%" },
|
||||
|
||||
7010: { uuid: 7010, name: "金币收益", icon: "Stat_InventorySlotIncrease", type: FieldSkillType.WaveGold, value: 1, info: "每回合金币收益+1" },
|
||||
7011: { uuid: 7011, name: "出售强化", icon: "Stat_GoldGainIncrease_01", type: FieldSkillType.SellGold, value: 1, info: "卖出英雄金币+1" },
|
||||
7012: { uuid: 7012, name: "购买优惠", icon: "Stat_KeyCapacityIncrease", type: FieldSkillType.BuyDiscount, value: 1, info: "购买卡牌费用-1金币" },
|
||||
7013: { uuid: 7013, name: "刷新优惠", icon: "Stat_RandomBonus", type: FieldSkillType.RefreshDiscount, value: 1, info: "刷新卡牌费用-1金币" },
|
||||
|
||||
7014: { uuid: 7014, name: "召唤强化", icon: "Stat_UnitSummonIncrease_02", type: FieldSkillType.SummonCount, value: 1, info: "召唤触发技能次数+1" },
|
||||
7015: { uuid: 7015, name: "死亡强化", icon: "Stat_PoisonChanceIncrease", type: FieldSkillType.DeadCount, value: 1, info: "死亡触发技能次数+1" },
|
||||
7016: { uuid: 7016, name: "开场强化", icon: "Stat_AttackRangeIncrease_01", type: FieldSkillType.StartCount, value: 1, info: "战斗开始触发技能次数+1" },
|
||||
7017: { uuid: 7017, name: "结束强化", icon: "Stat_UnitSummonIncrease_01", type: FieldSkillType.EndCount, value: 1, info: "战斗结束触发技能次数+1" },
|
||||
7018: { uuid: 7018, name: "攻击强化", icon: "Stat_Attack_03", type: FieldSkillType.AtkCount, value: 1, info: "攻击触发技能次数+1" },
|
||||
7019: { uuid: 7019, name: "受击强化", icon: "Stat_Armor_01", type: FieldSkillType.BeAtkCount, value: 1, info: "被攻击触发技能次数+1" },
|
||||
|
||||
// ============ wave5 档(原值 ×2) ============
|
||||
|
||||
7201: { uuid: 7201, name: "战后恢复+", icon: "Stat_PotionBoost", type: FieldSkillType.WaveHeal, value: 0.2, info: "战斗结束生命回复量+20%" },
|
||||
7202: { uuid: 7202, name: "攻击光环+", icon: "Stat_Attack_03", type: FieldSkillType.HeroAtk, value: 0.2, info: "英雄攻击力+20%" },
|
||||
7203: { uuid: 7203, name: "击晕光环+", icon: "Stat_Stun_01", type: FieldSkillType.HeroStun, value: 0.2, info: "英雄击晕概率+20%" },
|
||||
7204: { uuid: 7204, name: "暴击光环+", icon: "Stat_CriticalChance_02", type: FieldSkillType.HeroCrit, value: 0.2, info: "英雄暴击率+20%" },
|
||||
7205: { uuid: 7205, name: "暴伤光环+", icon: "Stat_Critical_01", type: FieldSkillType.HeroCritDamage, value: 0.4, info: "英雄暴击伤害+40%" },
|
||||
7206: { uuid: 7206, name: "攻速光环+", icon: "Stat_AttackSpeed_02", type: FieldSkillType.HeroSpeed, value: 0.2, info: "英雄攻击速度+20%" },
|
||||
7207: { uuid: 7207, name: "生命光环+", icon: "Stat_Hp_02", type: FieldSkillType.HeroHp, value: 0.2, info: "英雄最大生命+20%" },
|
||||
7208: { uuid: 7208, name: "风怒光环+", icon: "Stat_CriticalComboChance", type: FieldSkillType.HeroWindFury, value: 0.2, info: "英雄风怒概率+20%" },
|
||||
7209: { uuid: 7209, name: "穿刺光环+", icon: "Stat_Tripleshot", type: FieldSkillType.HeroPuncture, value: 0.2, info: "英雄穿刺概率+20%" },
|
||||
|
||||
|
||||
7210: { uuid: 7210, name: "金币收益+", icon: "Stat_InventorySlotIncrease", type: FieldSkillType.WaveGold, value: 2, info: "每回合金币收益+2" },
|
||||
7211: { uuid: 7211, name: "出售强化+", icon: "Stat_GoldGainIncrease_01", type: FieldSkillType.SellGold, value: 2, info: "卖出英雄金币+2" },
|
||||
7212: { uuid: 7212, name: "购买优惠+", icon: "Stat_KeyCapacityIncrease", type: FieldSkillType.BuyDiscount, value: 2, info: "购买卡牌费用-2金币" },
|
||||
7213: { uuid: 7213, name: "刷新优惠+", icon: "Stat_RandomBonus", type: FieldSkillType.RefreshDiscount, value: 2, info: "刷新卡牌费用-2金币" },
|
||||
|
||||
|
||||
|
||||
7401: { uuid: 7401, name: "战后恢复++", icon: "Stat_PotionBoost", type: FieldSkillType.WaveHeal, value: 0.3, info: "战斗结束生命回复量+30%" },
|
||||
7402: { uuid: 7402, name: "攻击光环++", icon: "Stat_Attack_03", type: FieldSkillType.HeroAtk, value: 0.3, info: "英雄攻击力+30%" },
|
||||
7403: { uuid: 7403, name: "击晕光环++", icon: "Stat_Stun_01", type: FieldSkillType.HeroStun, value: 0.3, info: "英雄击晕概率+30%" },
|
||||
7404: { uuid: 7404, name: "暴击光环++", icon: "Stat_CriticalChance_02", type: FieldSkillType.HeroCrit, value: 0.3, info: "英雄暴击率+30%" },
|
||||
7405: { uuid: 7405, name: "暴伤光环++", icon: "Stat_Critical_01", type: FieldSkillType.HeroCritDamage, value: 0.6, info: "英雄暴击伤害+60%" },
|
||||
7406: { uuid: 7406, name: "攻速光环++", icon: "Stat_AttackSpeed_02", type: FieldSkillType.HeroSpeed, value: 0.3, info: "英雄攻击速度+30%" },
|
||||
7407: { uuid: 7407, name: "生命光环++", icon: "Stat_Hp_02", type: FieldSkillType.HeroHp, value: 0.3, info: "英雄最大生命+30%" },
|
||||
7408: { uuid: 7408, name: "风怒光环++", icon: "Stat_CriticalComboChance", type: FieldSkillType.HeroWindFury, value: 0.3, info: "英雄风怒概率+30%" },
|
||||
7409: { uuid: 7409, name: "穿刺光环++", icon: "Stat_Tripleshot", type: FieldSkillType.HeroPuncture, value: 0.3, info: "英雄穿刺概率+30%" },
|
||||
|
||||
|
||||
7410: { uuid: 7410, name: "金币收益++", icon: "Stat_InventorySlotIncrease", type: FieldSkillType.WaveGold, value: 3, info: "每回合金币收益+3" },
|
||||
7411: { uuid: 7411, name: "出售强化++", icon: "Stat_GoldGainIncrease_01", type: FieldSkillType.SellGold, value: 3, info: "卖出英雄金币+3" },
|
||||
7412: { uuid: 7412, name: "购买优惠++", icon: "Stat_KeyCapacityIncrease", type: FieldSkillType.BuyDiscount, value: 3, info: "购买卡牌费用-3金币" },
|
||||
7413: { uuid: 7413, name: "刷新优惠++", icon: "Stat_RandomBonus", type: FieldSkillType.RefreshDiscount, value: 3, info: "刷新卡牌费用-3金币" },
|
||||
7014: { uuid: 7014, name: "召唤强化", icon: "Stat_UnitSummonIncrease_02", type: FieldSkillType.SummonCount, info: "召唤触发技能次数提升" },
|
||||
7015: { uuid: 7015, name: "死亡强化", icon: "Stat_PoisonChanceIncrease", type: FieldSkillType.DeadCount, info: "死亡触发技能次数提升" },
|
||||
7016: { uuid: 7016, name: "开场强化", icon: "Stat_AttackRangeIncrease_01", type: FieldSkillType.StartCount, info: "战斗开始触发技能次数提升" },
|
||||
7018: { uuid: 7018, name: "攻击强化", icon: "Stat_Attack_03", type: FieldSkillType.AtkCount, info: "攻击触发技能次数提升" },
|
||||
7019: { uuid: 7019, name: "受击强化", icon: "Stat_Armor_01", type: FieldSkillType.BeAtkCount, info: "被攻击触发技能次数提升" },
|
||||
7020: { uuid: 7020, name: "穿透强化", icon: "Stat_Tripleshot", type: FieldSkillType.HeroPunctureDmg, info: "英雄穿透后伤害提升(百分点)" },
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
361
assets/script/game/common/config/几何王国技能复刻方案.md
Normal file
361
assets/script/game/common/config/几何王国技能复刻方案.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# 几何王国技能复刻设计文档
|
||||
|
||||
> **目的**:基于《几何王国》热门英雄技能,分析哪些可以**零代码改动**或**最小改动**复刻到本系统(1 回合定生死机制)。
|
||||
>
|
||||
> **前置约束**(已确认的设计原则):
|
||||
> - 局内固定 lv=1,等级成长移交局外
|
||||
> - 单场战斗 30 秒
|
||||
> - card_lv 纯技能数量差异(1~5 个技能槽)
|
||||
> - 技能触发极简(`t_num=1`),高频高数值
|
||||
> - 无相同能力英雄,无阵容限制
|
||||
|
||||
---
|
||||
|
||||
## 一、本系统可复用的"触发骨架"
|
||||
|
||||
| 触发类型 | 枚举 | 语义 | 几何王国对应场景 |
|
||||
|---|---|---|---|
|
||||
| 战斗开始 | `SkillTriggerType.FStart` | 入场时触发 1 次 | 战吼、入场护盾 |
|
||||
| 战斗结束 | `SkillTriggerType.FEnd` | 战斗胜利后触发 | (30s 短局意义弱) |
|
||||
| 召唤入场 | `SkillTriggerType.Call` | 落地触发 1 次 | 登场技 |
|
||||
| 死亡触发 | `SkillTriggerType.Dead` | 死亡时触发 1 次 | 亡语、遗志 |
|
||||
| 攻击触发 | `SkillTriggerType.Atking` | 普攻 N 次触发 | 概率普攻特效(t_num 模拟概率) |
|
||||
| 受击触发 | `SkillTriggerType.Atked` | 被击 N 次触发 | 受击反击、叠甲 |
|
||||
| 驻场光环 | `SkillTriggerType.Field` | 存活期间全局生效 | 光环类(移速/攻击/回血) |
|
||||
| 复活 | `SkillTriggerType.Revive` | 死亡后复活 | 凤凰涅槃、尸骸存活 |
|
||||
|
||||
> **概率模拟技巧**:本系统无"概率触发"概念,用 `t_num` 计数模拟。
|
||||
> - `t_num=1`:每次普攻都触发(最高频)
|
||||
> - `t_num=3`:≈33% 概率
|
||||
> - `t_num=4`:≈25% 概率(对应几何王国的"30% 概率")
|
||||
|
||||
---
|
||||
|
||||
## 二、本系统可复用的"效果骨架"
|
||||
|
||||
### 2.1 SkillKind(技能主效果)
|
||||
|
||||
| 类型 | 字段 | 几何王国对应 |
|
||||
|---|---|---|
|
||||
| `SkillKind.Damage` | `ap` 百分比伤害 | 大招、战技伤害 |
|
||||
| `SkillKind.Heal` | `ap` 治疗量 | 治疗、吸血 |
|
||||
| `SkillKind.Shield` | `ap` 护盾层数(数值减免) | 暗光之盾、守护壁垒 |
|
||||
| `SkillKind.Support` | `buff_type` + `timed_buff_id` | 属性强化、限时增益 |
|
||||
| `SkillKind.Gold` | `gold` 金币数 | (本局不适用) |
|
||||
|
||||
### 2.2 击杀特效字段
|
||||
|
||||
| 字段 | 含义 | 几何王国对应 |
|
||||
|---|---|---|
|
||||
| `crt` | 额外暴击率 | 致命一击 |
|
||||
| `frz` | 额外冰冻概率 | 寒冰攻击 |
|
||||
| `stun` | 额外击晕概率 | 眩晕 |
|
||||
| `bck` | 击退距离增量 | 击退(雷霆一击、剑刃风暴) |
|
||||
| `hit_count` | 命中目标数 | 多段攻击(乱棍、剑刃风暴) |
|
||||
| `puncture_chance` | 穿透概率 | 穿透(暗器大师) |
|
||||
|
||||
### 2.3 BuffList 已有 DoT / HoT / 控制
|
||||
|
||||
| Buff ID | 名称 | 类型 | 几何王国对应 |
|
||||
|---|---|---|---|
|
||||
| 7002 | 灼烧 | Debuff DoT(-10 伤害/0.5s,吃 30% 攻击加成) | 剧毒利刃、火系持续伤害 |
|
||||
| 7003 | 冰冻 | Control(2s) | 冰冻 |
|
||||
| 7004 | 击晕 | Control(2s) | 眩晕、沉默(替代) |
|
||||
| 7010 | 再生 | Buff HoT(+10 治疗/0.5s) | 持续回血 |
|
||||
| 7011 | 攻击爆发 | Buff(+50% 攻击 5s) | 大圣归来、剑圣 1 觉 |
|
||||
| 7012 | 暴击爆发 | Buff(+30% 暴击 5s) | 暴击强化 |
|
||||
| 7017 | 击晕爆发 | Buff(+30% 击晕 5s) | 群体眩晕强化 |
|
||||
| 7111 | 战前强攻 | Buff(+30 攻击 5s) | 开场增益 |
|
||||
|
||||
### 2.4 FieldSkillType 已有光环
|
||||
|
||||
| 类型 | 几何王国对应 |
|
||||
|---|---|
|
||||
| `HeroAtk` | 攻击光环(人鱼公主、邪恶光环) |
|
||||
| `HeroCrit` / `HeroCritDamage` | 暴击光环 |
|
||||
| `HeroSpeed` | 攻速光环 |
|
||||
| `HeroHp` | 生命光环 |
|
||||
| `HeroStun` / `HeroWindFury` / `HeroPuncture` | 特殊光环 |
|
||||
| `WaveHeal` | 战后回血(30s 短局可改为局内 HoT) |
|
||||
|
||||
---
|
||||
|
||||
## 三、可直接复刻(零代码改动)
|
||||
|
||||
> 仅通过 `heroSet.ts` 配置 + 复用现有 SkillSet / BuffList 即可实现。
|
||||
|
||||
### 3.1 剧毒女王(Mage,复刻"深渊女王·剧毒利刃")
|
||||
|
||||
**核心机制**:普攻触发灼烧 DoT,叠加毒伤害。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 每次普攻上灼烧 DoT(已存在 BuffList 7002)
|
||||
6101: [{ lv: 1, t_num: 1, overrides: { timed_buff_id: 7002, buff_duration: 5 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:深渊女王·剧毒利刃(普攻 30% 概率 200% 法伤 + 减益)。
|
||||
**适配调整**:30s 战斗节奏快,`t_num=1` 100% 触发,但单层 DoT 数值调低以平衡。
|
||||
|
||||
---
|
||||
|
||||
### 3.2 猴王·乱棍(Assassin,复刻"至尊宝·大圣归来")
|
||||
|
||||
**核心机制**:普攻触发多段伤害+击晕。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 每次普攻触发 4 段 30% 伤害 + 高击晕
|
||||
6006: [{ lv: 1, t_num: 1, overrides: { ap: 30, hit_count: 4, stun: 30 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:至尊宝·大圣归来(乱棍 4 次 ×30% 物伤 + 眩晕)。
|
||||
**适配调整**:去除"召唤虚影+攻击+25%"(与本系统能量体系冲突),保留多段+击晕核心。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 剑圣·疾风(Warrior,复刻"剑圣·剑刃风暴+吸血面罩")
|
||||
|
||||
**核心机制**:高伤范围 + 击退 + 普攻吸血。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻触发 360% 范围伤害 + 击退
|
||||
6201: [{ lv: 1, t_num: 1, overrides: { ap: 150, bck: 30 } }],
|
||||
// 普攻吸血 35%
|
||||
6302: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Self, ap: 35 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:剑圣·剑刃风暴(360% 物伤 + 击退) + 吸血面罩(普攻 35% 吸血)。
|
||||
**适配调整**:`t_num=1` 高频触发,单场 30s 内吸血量需调平衡。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 壁垒贤者(Support,复刻"哀木涕·守护壁垒")
|
||||
|
||||
**核心机制**:开场全队护盾 + 驻场生命光环。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.FStart]: {
|
||||
// 开场全队护盾(数值减免型)
|
||||
6301: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } }],
|
||||
},
|
||||
[SkillTriggerType.Field]: [
|
||||
// 驻场生命光环
|
||||
{ lv: 1, s_uuid: 7007, value: 15 }, // +15% 最大生命
|
||||
],
|
||||
```
|
||||
|
||||
**复刻自**:哀木涕·守护壁垒(开场 10% 最大生命护盾 + 防御光环)。
|
||||
**适配调整**:本系统护盾是"数值减免"非"层数",ap 字段意义不同,需重新校准数值。
|
||||
|
||||
---
|
||||
|
||||
### 3.5 凤凰祭司(Warrior,复刻"死亡守护 + 复活")
|
||||
|
||||
**核心机制**:死亡时为最低血友军回血 + 自身可复活。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Dead]: {
|
||||
// 死亡守护:死亡时为最低血友军回血
|
||||
6302: [{ lv: 1, t_num: 1, overrides: { ap: 50 } }],
|
||||
},
|
||||
[SkillTriggerType.Revive]: [
|
||||
// 复活:死亡后 1 次 50% 血复活
|
||||
{ lv: 1, s_uuid: 6501, r_num: 1, upr: 50 },
|
||||
],
|
||||
```
|
||||
|
||||
**复刻自**:通用觉醒·死亡守护(10% 最大生命回血) + 黑暗骑士 3 觉(尸骸存活)。
|
||||
**适配调整**:30s 战斗中复活价值高,`r_num=1` 限定 1 次。
|
||||
|
||||
---
|
||||
|
||||
### 3.6 暗影骑士(Tank,复刻"黑暗骑士·死亡缠绕")
|
||||
|
||||
**核心机制**:普攻治疗血少友军 + 受击反击自盾。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻触发治疗(自动选血量最低友方,引擎已支持)
|
||||
6302: [{ lv: 1, t_num: 1, overrides: { ap: 220 } }],
|
||||
},
|
||||
[SkillTriggerType.Atked]: {
|
||||
// 受击触发自身护盾
|
||||
6301: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Self, ap: 3 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:黑暗骑士·死亡缠绕(普攻 25% 概率 220% 真伤/治疗友方)。
|
||||
**适配调整**:本系统暂无"真伤",用高治疗量替代。
|
||||
|
||||
---
|
||||
|
||||
### 3.7 风暴歌者(Mage,复刻"深渊女王·超声波")
|
||||
|
||||
**核心机制**:范围伤害 + 群体击退。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 范围伤害 + 群体击退
|
||||
6201: [{ lv: 1, t_num: 1, overrides: { ap: 200, bck: 50, hit_count: 6 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:深渊女王·超声波(伤害 + 击退控制)。
|
||||
**适配调整**:`bck` 字段直接对应击退距离增量,无需新增机制。
|
||||
|
||||
---
|
||||
|
||||
### 3.8 战吼先锋(Warrior,复刻"光明骑士·光明守护")
|
||||
|
||||
**核心机制**:开场全队增益 + 持续回血。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.FStart]: {
|
||||
// 开场全队攻击强化
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { ap: 30 } }],
|
||||
// 开场全队持续回血
|
||||
6502: [{ lv: 1, t_num: 1, overrides: { timed_buff_id: 7010, buff_duration: 10 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:光明骑士·光明守护(清异常 + 加防 + 持续治疗)。
|
||||
**适配调整**:本系统无"解控"概念,30s 战斗也不需要,改为纯增益 + HoT。
|
||||
|
||||
---
|
||||
|
||||
## 四、需要小幅扩展即可复刻
|
||||
|
||||
### 4.1 反伤罗刹(Tank,复刻"剑豪·罗生门")
|
||||
|
||||
**缺失机制**:本系统无"反伤"属性。
|
||||
|
||||
**扩展方案**:
|
||||
1. `HeroAttrs.ts` 的 `Attrs` 枚举新增 `thorns = "thorns"`(反伤数值)
|
||||
2. `HeroAttrsComp.ts` 新增字段 `thorns: number = 0`
|
||||
3. 战斗结算时,受击方对攻击者造成 `thorns` 点固定伤害
|
||||
4. `SkillConfig` 新增 `thorns?: number` 用于 Support 技能附加反伤 buff
|
||||
|
||||
**复刻配置**:
|
||||
```typescript
|
||||
[SkillTriggerType.Atked]: {
|
||||
// 受击触发护盾 + 反伤 buff(5s 内反伤 30 点)
|
||||
6301: [{ lv: 1, t_num: 1, overrides: { ap: 5 } }],
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { buff_type: Attrs.thorns, timed_buff_id: 7030 } }],
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 真伤刺客(Assassin,复刻"玉面狐妖·真伤")
|
||||
|
||||
**缺失机制**:本系统伤害公式只支持 `ap × 系数`,无"按目标最大生命百分比伤害"。
|
||||
|
||||
**扩展方案**:
|
||||
1. `SkillConfig` 新增 `target_hp_pct?: number`(按目标最大生命百分比扣血)
|
||||
2. 伤害结算时,若技能含此字段,额外造成 `target.hp_max × target_hp_pct / 100` 伤害
|
||||
|
||||
**复刻配置**:
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻附带 3% 目标最大生命真伤
|
||||
6001: [{ lv: 1, t_num: 1, overrides: { ap: 100, /* 需新增 target_hp_pct: 3 */ } }],
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3 重伤诅咒(Mage,复刻"巫妖王·重伤")
|
||||
|
||||
**缺失机制**:本系统无"降低治疗效果"属性。
|
||||
|
||||
**扩展方案**:
|
||||
1. `Attrs` 新增 `heal_reduce = "heal_reduce"`(百分比降低受治疗)
|
||||
2. 治疗结算时,实际治疗量 = 原治疗量 × (1 - heal_reduce/100)
|
||||
|
||||
**复刻配置**:
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻附带重伤 debuff(敌方受治疗 -50%)
|
||||
6101: [{ lv: 1, t_num: 1, overrides: { /* 需新增 heal_reduce 机制 */ } }],
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、不建议复刻
|
||||
|
||||
| 类型 | 代表技能 | 原因 |
|
||||
|---|---|---|
|
||||
| **能量/大招系统** | 所有英雄的"大招" | 本系统无能量条,技能全靠触发器,无法承载"释放大招"概念 |
|
||||
| **解控** | 光明洗礼、暗黑盾解控 | 30s 战斗节奏快,控制时间仅 2~3s,解控价值极低 |
|
||||
| **多段位移/分身** | 炼狱鬼斩、大圣虚影 | 视觉表现层复杂,ECS 弹道模型难承载"幻影穿梭" |
|
||||
| **沉默 / 减能** | 潮壮壮、海拉 | 本系统无主动技能,沉默无目标可作用 |
|
||||
| **长期叠层** | 剑圣 1 觉(30% 防御转攻击,永久) | 30s 战斗内永久叠层易失衡 |
|
||||
| **按当前生命%伤害** | 女王尖啸(18% 当前生命) | 与"按最大生命%"相比,30s 短局内波动过大,难平衡 |
|
||||
| **击退抗性 / 减速抗性** | 剑豪红武、酒仙 | 本系统已有 `knockback_res`,但减速概念不存在 |
|
||||
|
||||
---
|
||||
|
||||
## 六、复刻优先级矩阵
|
||||
|
||||
| 优先级 | 英雄 | 复刻难度 | 设计价值 |
|
||||
|---|---|---|---|
|
||||
| **P0(必做)** | 剧毒女王 | ✅ 零代码 | 引入 DoT 体系,丰富伤害维度 |
|
||||
| **P0** | 猴王·乱棍 | ✅ 零代码 | 多段伤害+击晕,近战核心 |
|
||||
| **P0** | 剑圣·疾风 | ✅ 零代码 | 范围+击退+吸血,全能战士 |
|
||||
| **P1(推荐)** | 壁垒贤者 | ✅ 零代码 | 团队防御核心 |
|
||||
| **P1** | 凤凰祭司 | ✅ 零代码 | 复活机制已有,强化死亡触发 |
|
||||
| **P1** | 暗影骑士 | ✅ 零代码 | 治疗型坦克,差异化定位 |
|
||||
| **P2(可选)** | 风暴歌者 | ✅ 零代码 | 范围控制,与剧毒女王互补 |
|
||||
| **P2** | 战吼先锋 | ✅ 零代码 | 开场增益,节奏型辅助 |
|
||||
| **P3(扩展后做)** | 反伤罗刹 | ⚙️ 需新增 thorns | 反伤 META,针对高攻速敌人 |
|
||||
| **P3** | 真伤刺客 | ⚙️ 需新增 target_hp_pct | 针对高血量 Boss |
|
||||
| **P3** | 重伤诅咒 | ⚙️ 需新增 heal_reduce | 针对治疗队 |
|
||||
|
||||
---
|
||||
|
||||
## 七、新增机制工作量评估
|
||||
|
||||
| 机制 | 改动文件 | 改动量 | 风险 |
|
||||
|---|---|---|---|
|
||||
| **反伤 thorns** | HeroAttrs.ts + HeroAttrsComp.ts + 战斗结算 | ~50 行 | 低(独立属性) |
|
||||
| **按最大生命%伤害** | SkillSet.ts + 伤害结算 | ~30 行 | 低(额外乘区) |
|
||||
| **减疗 heal_reduce** | HeroAttrs.ts + HeroAttrsComp.ts + 治疗结算 | ~40 行 | 低(独立乘区) |
|
||||
| **敌方 Support 技能** | SCastSystem.ts | ~20 行 | 中(需测试目标选择) |
|
||||
|
||||
---
|
||||
|
||||
## 八、与原"20 回合制"的兼容性
|
||||
|
||||
本设计文档基于 **1 回合定生死** 机制,所有数值均按 **30 秒战斗窗口** 校准:
|
||||
|
||||
- `t_num=1` 触发:30s 内普攻 30~50 次,技能触发 30~50 次
|
||||
- DoT/HoT 持续 5s:覆盖 1/6 战斗时长,效果显著但不至于"挂上就赢"
|
||||
- 复活 `r_num=1`:单场战斗最多 1 次,价值极高但可控
|
||||
- 驻场光环 `value=15%`:30s 内持续生效,等价于"永久"(单场)
|
||||
|
||||
若未来回归多回合制,需重新校准:
|
||||
- `t_num` 提高到 2~4(降低触发频率)
|
||||
- DoT/HoT 持续时间延长至 10~15s
|
||||
- 复活次数可放开到 2~3 次
|
||||
|
||||
---
|
||||
|
||||
## 九、下一步行动
|
||||
|
||||
1. **确认 P0 英雄清单**:是否按本方案复刻 8 个英雄?
|
||||
2. **数值校准**:是否需要先用 `calcHeroPower` 公式推导每个英雄的目标强度锚点?
|
||||
3. **新增机制决策**:是否在第 1 阶段引入反伤/真伤/减疗?还是先纯配置复刻?
|
||||
4. **编写 heroSet.ts 配置**:确认后我可以直接输出可粘贴的 TypeScript 配置代码。
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:v1.0
|
||||
**创建日期**:2026-08-14
|
||||
**作者**:AI Assistant
|
||||
**关联文档**:[heroSet.ts](./heroSet.ts) / [SkillSet.ts](./SkillSet.ts) / [BuffSet.ts](./BuffSet.ts) / [几何王国英雄技能汇总.md](./几何王国英雄技能汇总.md)
|
||||
11
assets/script/game/common/config/几何王国技能复刻方案.md.meta
Normal file
11
assets/script/game/common/config/几何王国技能复刻方案.md.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"importer": "text",
|
||||
"imported": true,
|
||||
"uuid": "4b91da8c-0510-4f36-a3f7-b78ec260c1f9",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
199
assets/script/game/common/config/几何王国英雄技能汇总.md
Normal file
199
assets/script/game/common/config/几何王国英雄技能汇总.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# 《几何王国》(微信小游戏)英雄技能汇总
|
||||
|
||||
> **资料说明**:本作暂无官方公开的完整技能数据库,以下内容整理自玩家攻略站、公众号「英雄解析」系列、社区讨论等公开资料(来源见文末)。技能数值可能随版本更新调整,请以游戏内实际为准。游戏现有 45+ 英雄,本文收录主流热门英雄。
|
||||
|
||||
---
|
||||
|
||||
## 一、技能体系说明
|
||||
|
||||
每个英雄的技能按养成线逐步解锁 [^7^][^27^]:
|
||||
|
||||
| 解锁节点 | 技能类型 |
|
||||
|---|---|
|
||||
| 初始 | 大招(主动技能) |
|
||||
| 2 星 | 战技技能 |
|
||||
| 5 星 | 强化技能 |
|
||||
| 8 星 | 特技技能 |
|
||||
| 12 星 | 属性技能 |
|
||||
| 16 星 | 强化技能 |
|
||||
| 觉醒 | 一觉 ~ 四觉(部分为通用技能池) |
|
||||
| 神器 | 紫 / 黄 / 红 / 彩 四档神器加成 |
|
||||
|
||||
---
|
||||
|
||||
## 二、热门英雄详细技能
|
||||
|
||||
### 1. 黑暗骑士(神话 · 前排主坦,版本必备)
|
||||
|
||||
- **大招「暗光之盾」**:释放无视控制;获得 8% 最大生命值的护盾并转化为生命(持续 7 秒),其中 5% 作用于队友;提供 20% 减速抗性 [^18^]
|
||||
- **缠绕(死亡缠绕)**:普攻有 25% 几率对全场血量最低的目标施放——敌方目标受到 220%(+10% 神器增强)真实伤害,友方目标回复等量生命 [^18^][^75^]
|
||||
- **暗黑盾**:解除控制,且附带击退效果 [^18^]
|
||||
- **神器「霜之暗斧」**:叠加 3 层可沉默敌方前排并减速 [^18^]
|
||||
- **彩色神器「邪恶光环」**:+8% 移动速度光环 + 少量回复 [^18^]
|
||||
- **觉醒**:1 觉全队 +10% 魔抗;3 觉阵亡后以尸体状态存活 3.5 秒,为后排争取输出时间 [^18^]
|
||||
- **定位评价**:目前版本最全面的前排,治疗、推进、解控兼具,基本是必备品 [^18^]
|
||||
|
||||
### 2. 光明骑士(神话 · 前排辅助)
|
||||
|
||||
- **大招「光明守护」**:释放无视控制,清除全队异常状态;全队 +30% 防御、+10% 能量回复速度;每秒 35% 攻击力的治疗,持续 7 秒 [^18^]
|
||||
- **圣洁之锤**:120% 真实伤害,伤害的 70% 转化为自身治疗 [^18^]
|
||||
- **光明洗礼**:每 6 秒触发一次,为 2 名队友解除异常状态(可解沉默、控制、减攻等)[^18^]
|
||||
- **彩色神器「圣域守护」**:减少敌方 20% 攻速、10% 移速;每 10 秒给队友单体释放光明守护 [^18^]
|
||||
|
||||
### 3. 剑圣(神话 · 前排输出)
|
||||
|
||||
- **大招「剑刃风暴」**:360% 物理伤害,击退所有角色,附带 6 秒 20% 重伤 [^68^]
|
||||
- **战技「致命一击」**:普攻 35% 几率造成 1.5~2.2 倍真实伤害 [^68^]
|
||||
- **分身斩**:每隔一段时间造成 5 次伤害 [^68^]
|
||||
- **觉醒**:1 觉将 30% 防御转换为攻击;3 觉普攻随机降低对方攻击 [^68^]
|
||||
- **评价**:免疫技能、群体击退,能聚集敌方后排利于己方 AOE;但伤害多为打前排的"无效输出",定位偏功能性抗伤 + 击退 [^68^]
|
||||
|
||||
### 4. 酒仙(神话 · 为推进而生)
|
||||
|
||||
- **风暴战士**:清除负面效果,击退抗性 +100%,伤害减免 30%(无法减免真伤),75% 免控 [^68^]
|
||||
- **雷霆一击**:普攻 30% 概率使范围内敌人全体减速并强力击退 [^68^]
|
||||
- **酒雾火焰**:每隔一段时间造成伤害并附加 5 秒 50% 重伤效果 [^68^]
|
||||
- **觉醒**:1 觉普攻随机降低目标 6% 攻击或 10% 防御;3 觉血量低于 50% 时,5 秒免疫魔法或物理伤害 [^68^]
|
||||
|
||||
### 5. 剑豪(前排战士 · 针对射手/物理队)
|
||||
|
||||
作用:输出 + 沉默 + 减攻 + 减退抗 + 反伤;BUFF/DEBUFF 全面,进可攻退可守,缺点是缺少硬控 [^7^]
|
||||
|
||||
- **大招「炼狱鬼斩」**:制造幻影在战场穿梭发动 6 次斩击,每次 360% 攻击的物理伤害并附带击退,每个目标至多承受 3 次,优先斩击物理伤害单位 [^7^]
|
||||
- **2 星战技「三十六烦恼凤」**:每 10 秒释放剑气攻击敌方能量最高单位,500% 物理伤害 + 沉默 1.5 秒(可抢第一波大招先手)[^7^]
|
||||
- **5 星强化「强化斩击」**:大招命中攻击高于自己的目标时,降低其 15% 攻击 6 秒 [^7^]
|
||||
- **8 星特技「罗生门」**:每累计损失 25% 最大生命,生成剑门屏障,抵消 18% 伤害 + 25% 反伤,持续 4 秒,无冷却(核心技能)[^7^]
|
||||
- **12 星「武装色霸气」**:攻击 +10%、生命 +15% [^7^]
|
||||
- **16 星「修罗之道」**:大招每次斩击额外造成 100% 真实伤害,并降目标 15% 击退抗性 5 秒 [^7^]
|
||||
- **神器**:紫武(战技沉默结束后降目标 30% 回能 5 秒);黄武(罗生门反伤增至 55%、持续增至 6 秒);**红武「三千世界」**(入场 13 秒后目标承受伤害 +27%,自身受沉默时间 -60%);彩武(罗生门释放时立即回复 11% 最大生命 + 回能/回血加成)[^7^]
|
||||
- **觉醒「空手接白刃」**:生命低于 55% 时,永久降低敌方全体 15% 物理伤害直至阵亡 [^7^]
|
||||
- **培养建议**:神器最低门槛红色,推荐彩色,优先级高 [^7^]
|
||||
|
||||
### 6. 至尊宝(前排战士 · 功能性工具人)
|
||||
|
||||
作用:击退 + 眩晕 + 减防 + 受伤回血 + 扛伤;能抗能解控,劣势局有反打之力,但输出低 [^27^]
|
||||
|
||||
- **大招「大圣归来」**:召唤大圣虚影,期间自身攻击 +25%;乱棍打击 4 次 ×30% 物理伤害并击退,收尾一击 160% 物理伤害 + 2 秒眩晕 [^27^]
|
||||
- **2 星战技「七伤拳」**:普攻 30% 概率降低目标 30% 防御,但 20% 概率降低自身 10% 防御 [^27^]
|
||||
- **5 星强化「大圣之力」**:放大招时自身 +15% 击退效果 3 秒 [^27^]
|
||||
- **8 星特技「般若波罗蜜」**:每 12 秒引导时空之力,3 秒内受到伤害的 60% 转化为自身生命(不受重伤影响,转化总量不超过 12% 最大生命)[^27^]
|
||||
- **12 星「五岳之尊」**:防御 +10%、生命 +15%;**16 星「大圣之御」**:放大招时受伤 -30% 持续 6 秒 [^27^]
|
||||
- **神器**:紫武(七伤拳触发概率 +10%);黄武(般若波罗蜜可作用于所有近战友军)[^27^]
|
||||
|
||||
### 7. 齐天大圣(神话 · T0 全能战士,人气主 C)
|
||||
|
||||
- **主动技能「定海神针」**:对敌方前排造成范围眩晕效果,打乱敌方阵型 [^35^]
|
||||
- 技能与普攻均可造成高额暴击伤害,后期伤害依然可观 [^35^][^33^]
|
||||
- 强度榜长期位居 T0,获取与培养成本低,新手友好 [^33^][^37^]
|
||||
|
||||
### 8. 复仇者(神话 · T0 后排杀手)
|
||||
|
||||
- **「暗影突袭」**:可穿透威胁敌方后排关键目标 [^2^]
|
||||
- 攻击敌人附加流血效果;获取成本较低,性价比极高 [^33^][^1^]
|
||||
- 阵容中可搭配火枪,用斩击 + 狙击双重伤害秒杀敌方后排 [^14^]
|
||||
|
||||
### 9. 深渊女王 / 深渊女巫(法师 · 群体输出 + 控制)
|
||||
|
||||
- **大招「超声波」**:主力伤害 + 击退控制技能 [^60^]
|
||||
- **2 星战技「剧毒利刃」**:普攻 30% 几率射出淬毒匕首,200% 魔法伤害,3 秒内降低目标 20% 击退抗性 [^28^]
|
||||
- **5 星强化「强化声波」**:超声波击退效果 +30% [^28^]
|
||||
- **8 星特技「女王尖啸」**:战斗第 20 秒发出尖啸,使所有敌方目标失去 18% 当前生命并眩晕 2 秒(每场仅 1 次,上限 350% 攻击)[^28^]
|
||||
- **12 星「攻击增强」**:攻击 +25%;**16 星「超级声波」**:超声波魔法伤害 +65%,被击中目标 5 秒内伤害结果 -25% [^28^]
|
||||
|
||||
### 10. 巫妖王(法师 · 当前版本核心)
|
||||
|
||||
- **核心能力是红武提供的"重伤"**——这种能力全英雄中稀缺,在爷爷、光明骑士遍地走的环境下接近必需品 [^3^]
|
||||
- 彩神具有**多段控制**(连环霜冻,黑暗骑士与光明骑士都解控不过来)[^3^]
|
||||
- 自带**减速和冰冻能力**,利于推进甚至壁咚,集控制、输出、重伤、推力于一体 [^3^]
|
||||
- 常见搭配:巫妖王 + 黑暗骑士 + 光明骑士 + 战士(复仇者/哪吒/剑圣/船长)+ 暗器大师或影魔 [^3^]
|
||||
|
||||
### 11. 玉面狐妖(法师 · 真伤输出)
|
||||
|
||||
- 前排杀手队核心之一:真实伤害无视防御,配合爷爷的攻击与回能增益可快速消灭敌方前排 [^14^]
|
||||
- 后期值得培养的法师主 C 之一 [^1^]
|
||||
|
||||
---
|
||||
|
||||
## 三、英雄觉醒技能表
|
||||
|
||||
以下为各英雄觉醒技能(一觉~四觉,含通用技能池)[^4^]:
|
||||
|
||||
| 英雄 | 觉醒技能 |
|
||||
|---|---|
|
||||
| **斧小王** | 坚硬皮肤(受伤 +1.5% 防御,最多 12 层);梦魇肩甲;死亡守护;嗜血战斧(每次释放「淘汰之斧」回复 15% 已损生命) |
|
||||
| **剑圣** | 吸血面罩(普攻伤害的 35% 转化为自身生命);攻防转换(30% 防御转攻击,50% 几率降敌 5% 攻击 5 秒);梦魇肩甲;死亡守护 |
|
||||
| **剑豪** | 空手接白刃(生命低于 55% 时永久降敌方全体 15% 物理伤害);梦魇肩甲;死亡守护;见闻色霸气(己方半场 +22% 击退抗性 / 敌方半场 +22% 控制抗性) |
|
||||
| **酒仙** | 重拳(普攻随机降 6% 攻击或降 10% 防御 5 秒);元素抗性(血量首次低于 50%,5 秒免疫物理或魔法伤害) |
|
||||
| **哪吒** | 三味真火(攻击附加 3% 最大生命伤害,上限 150% 攻击);莲花护体(每损失 10% 生命 +10% 受伤回能) |
|
||||
| **八神** | 狂暴之怒(15% 伤害转化为自身生命);疯狂之血(血量低于 50%,+5% 攻击 +5% 破甲) |
|
||||
| **哀木涕** | 守护壁垒(开场获得 10% 最大生命护盾,自身与身后友军防御 +6%);战斗意志(每累计损失 20% 生命 +10% 受伤回能 +5% 格挡,最多 6 层) |
|
||||
|
||||
**通用觉醒技能**:
|
||||
|
||||
- **梦魇肩甲**:受到攻击时,使攻击者 6 秒内对自己造成的伤害降低 2%(最多 3 层)[^4^]
|
||||
- **死亡守护**:自身死亡时,为血量最低的友军回复 10% 最大生命值 [^4^]
|
||||
|
||||
---
|
||||
|
||||
## 四、其他英雄技能速览
|
||||
|
||||
| 英雄 | 定位 | 技能特点 |
|
||||
|---|---|---|
|
||||
| 骷髅王 | 前排/输出 | 大招对敌方全体施加诅咒 + 暴击伤害,通用性强 [^33^] |
|
||||
| 月之女 | 输出 | 爆发能力极强,技能持续叠加伤害,每次释放输出更高 [^33^][^47^] |
|
||||
| 潮壮壮 | 前排/控制 | 技能命中造成沉默,让敌人无法放技能;攻防兼备,泛用性强,PVP 必备且不太吃练度 [^33^][^47^] |
|
||||
| 火枪 | 射手 | 「狙击」致命打击敌方后排,与复仇者斩击配合可双重秒杀后排 [^14^] |
|
||||
| 船长 | 战士 | 幽灵船、舰船突袭:高额伤害 + 大幅击退,暴力推进队核心 [^14^] |
|
||||
| 术士 | 辅助 | 地狱火有强大推进能力并可作前排分摊伤害;技能带减能、减疗、减速 [^14^] |
|
||||
| 海拉 | 法师 | 技能带减能、减疗、沉默,控制折磨队核心 [^14^] |
|
||||
| 沉默 | 辅助 | 技能带减能、沉默效果 [^14^] |
|
||||
| 疾风忍者 | 前排 | 技能带减能、沉默效果 [^14^] |
|
||||
| 铁扇公主 | 法师 | 推进 + 以自身为中心约 2 个人物身位的光环 [^68^] |
|
||||
| 神罗战士 / 李白 | 战士 | 带真实伤害,无视防御,前排杀手 [^14^] |
|
||||
| 人鱼公主 | 辅助 | 提供控制,并为全队增加攻击力加成,提升队伍输出 [^1^][^39^] |
|
||||
| 莫莫 | 辅助 | 为队友提供减防支持,提升队伍生存与输出 [^1^] |
|
||||
| 月光使者 | 辅助 | 黄武可用,开荒期过渡英雄 [^3^] |
|
||||
| 爷爷(葫芦爷爷) | 辅助 | 为队友回能 + 加攻击,强化核心输出伤害 [^14^] |
|
||||
| 暗器大师 / 影魔 | 射手 | 巫妖王体系最推荐的射手位 [^3^] |
|
||||
|
||||
---
|
||||
|
||||
## 五、英雄强度榜(参考)
|
||||
|
||||
综合多个攻略站的强度评价 [^33^][^37^][^34^]:
|
||||
|
||||
- **T0**:复仇者、齐天大圣(另有榜单列入影刃、月之女)
|
||||
- **T1**:骷髅王、月之女、绿巨人
|
||||
- **T1.5**:剑圣
|
||||
- **T2**:潮壮壮、暗器大师、矮人飞机、双头龙
|
||||
- **T3**:光明骑士、术士(注:此榜为旧版评价,当前版本光明骑士、术士在特色阵容中仍有重要地位 [^14^])
|
||||
|
||||
**后期值得培养的主 C 推荐** [^1^]:
|
||||
|
||||
- 战士:船长、黑暗骑士、复仇者、剑圣、神罗战士、白牛
|
||||
- 辅助:冰女、小鹿、光明骑士、琴音仙女、术士、月光使者、沉默
|
||||
- 射手:火枪、影魔、骷髅射手、暗器大师
|
||||
- 法师:巫妖王、玉面狐妖、海拉、铁扇公主、深渊女王
|
||||
|
||||
---
|
||||
|
||||
## 参考来源
|
||||
|
||||
[^1^]: [几何王国游戏攻略/钓鱼/鱼缸搭配/英雄推荐 — 灯塔码铺](https://blog.share888.top/note/game/jihewangguo/index.html)
|
||||
[^2^]: [《几何王国》——快速养成,满满成就!— 小米游戏中心](https://game.xiaomi.com/viewpoint/1585024444_1751347565577_100)
|
||||
[^3^]: [《几何王国》该如何确定"终极"阵容和一些杂谈 — 雷电模拟器](https://www.ldmnq.com/0/zixun/1028749.html)
|
||||
[^4^]: [几何王国英雄觉醒技能表 — 7724游戏](http://www.7724.com/jhwg/news/188085.html)
|
||||
[^7^]: [【几何王国】英雄解析--剑豪 — 微信公众号](http://mp.weixin.qq.com/s?__biz=MzkxMjU1NTA2NA==&mid=2247485616&idx=1&sn=50f23d36776ee8d25f5916dee600caf7)
|
||||
[^14^]: [《几何王国》新英雄与新玩法背景下,如何搭配适应版本的多样阵容 — 雷电模拟器](https://m.ldmnq.com/0/zixun/1028757.html)
|
||||
[^18^]: [觉醒后英雄强度分析-前排篇(I)— 4399](https://a.4399.cn/gl/50173882_361641.html)
|
||||
[^27^]: [【几何王国】英雄解析--至尊宝 — 微信公众号](http://mp.weixin.qq.com/s?__biz=MzkxMjU1NTA2NA==&mid=2247485630&idx=1&sn=5df2e8c053ab873e78ecfae5db7dfcb3)
|
||||
[^28^]: [《几何王国》英雄攻略深渊女巫 — 百度贴吧](https://tieba.baidu.com/p/9105304613)
|
||||
[^33^]: [几何王国角色强度排行 — 3DM手游](https://shouyou.3dmgame.com/gl/517771.html)
|
||||
[^34^]: [几何王国角色排行表大全2025英雄强度排名 — 7724游戏](http://m.7724.com/jhwg/news/183870.html)
|
||||
[^35^]: [几何王国社区 — 小米游戏中心](https://game.xiaomi.com/gamePost/62405456)
|
||||
[^37^]: [几何王国角色强度排行 — 游侠手游](https://m.ali213.net/news/gl2404/1370787.html)
|
||||
[^39^]: [《几何王国》新手进阶全攻略 — 百度文库](https://word.baidu.com/view/e9324c26b8d126fff705cc1755270722192e5990.html)
|
||||
[^47^]: [几何王国最强英雄推荐 t0英雄强度排行 — 16手游网](http://www.16shouyou.com/gl/34278.html)
|
||||
[^60^]: [深渊女王的三个阶段分别是什么?— 百度贴吧·几何王国吧](https://tieba.baidu.com/p/9646144609)
|
||||
[^68^]: [《几何王国》觉醒后英雄强度分析-前排篇(I)— 雷电模拟器](https://m.ldmnq.com/0/zixun/1028753.html)
|
||||
[^75^]: [几何王国绝顶英雄主推2024 — 悟极手游网](https://www.wjmplastics.com/article/223.html)
|
||||
11
assets/script/game/common/config/几何王国英雄技能汇总.md.meta
Normal file
11
assets/script/game/common/config/几何王国英雄技能汇总.md.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"importer": "text",
|
||||
"imported": true,
|
||||
"uuid": "3ec17428-df2e-40bc-a707-4de99ee79b10",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { FacSet } from "../common/config/GameSet";
|
||||
import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet";
|
||||
import { FieldEntry } from "../common/config/heroSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { SkillBoxComp } from "../map/SkillBoxComp";
|
||||
import { EquipBoxComp } from "../map/EquipBoxComp";
|
||||
|
||||
/**
|
||||
* 驻场技能(光环属性)计算辅助类
|
||||
*
|
||||
*
|
||||
* 核心职责:
|
||||
* 统一管理全场存活英雄带来的全局加成属性计算。
|
||||
* 解耦原先写在 HeroAttrsComp 中的静态计算逻辑,符合单一职责原则。
|
||||
*
|
||||
* 数据结构:field 列表元素为 FieldEntry({uuid,value}),uuid 仅用于查 FieldSkillSet
|
||||
* 的类型元数据,实际加成数值取自词条自身的 value。
|
||||
*/
|
||||
export class FieldSkillHelper {
|
||||
/** 获取指定驻场技能类型的总加成值(计算存活的友方英雄 + 场上的驻场技能卡) */
|
||||
@@ -27,47 +31,37 @@ export class FieldSkillHelper {
|
||||
public static getFieldSkillTotalValueForFac(type: FieldSkillType, fac: number = FacSet.HERO): number {
|
||||
let total = 0;
|
||||
|
||||
// 累加一组驻场词条中匹配类型的数值
|
||||
const accumulate = (fields: FieldEntry[] | undefined): void => {
|
||||
if (!fields) return;
|
||||
for (const entry of fields) {
|
||||
const fs = FieldSkillSet[entry.uuid];
|
||||
if (fs && fs.type === type) {
|
||||
total += entry.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 1. 统计英雄/怪物带来的驻场技能加成
|
||||
// 读 model.runtime_field(已按当前等级 resolve 的 uuid 列表)
|
||||
// 读 model.runtime_field(已按当前等级 resolve 的词条列表)
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||
const model = entity.get(HeroAttrsComp);
|
||||
if (!model || model.is_dead || model.fac !== fac) return;
|
||||
const fields = model.runtime_field;
|
||||
if (fields) {
|
||||
for (const skillUuid of fields) {
|
||||
const skillConfig = FieldSkillSet[skillUuid];
|
||||
if (skillConfig && skillConfig.type === type) {
|
||||
total += skillConfig.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
accumulate(model.runtime_field);
|
||||
});
|
||||
|
||||
// 2. 统计技能盒子(技能卡)带来的驻场技能加成(仅英雄阵营)
|
||||
// 2. 统计技能盒子(技能卡)与装备盒(装备卡)带来的驻场技能加成(仅英雄阵营)
|
||||
if (fac === FacSet.HERO) {
|
||||
ecs.query(ecs.allOf(SkillBoxComp)).forEach((entity: ecs.Entity) => {
|
||||
const skillBox = entity.get(SkillBoxComp);
|
||||
if (!skillBox || !skillBox.field || skillBox.field.length === 0) return;
|
||||
|
||||
for (const skillUuid of skillBox.field) {
|
||||
const skillConfig = FieldSkillSet[skillUuid];
|
||||
if (skillConfig && skillConfig.type === type) {
|
||||
total += skillConfig.value;
|
||||
}
|
||||
}
|
||||
if (!skillBox) return;
|
||||
accumulate(skillBox.field);
|
||||
});
|
||||
|
||||
// 3. 统计装备盒(装备卡)带来的驻场技能加成(仅英雄阵营)
|
||||
ecs.query(ecs.allOf(EquipBoxComp)).forEach((entity: ecs.Entity) => {
|
||||
const equipBox = entity.get(EquipBoxComp);
|
||||
if (!equipBox || !equipBox.field || equipBox.field.length === 0) return;
|
||||
|
||||
for (const skillUuid of equipBox.field) {
|
||||
const skillConfig = FieldSkillSet[skillUuid];
|
||||
if (skillConfig && skillConfig.type === type) {
|
||||
total += skillConfig.value;
|
||||
}
|
||||
}
|
||||
if (!equipBox) return;
|
||||
accumulate(equipBox.field);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { smc } from "../common/SingletonModuleComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
|
||||
import { HeroInfo, HeroPos, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, calcGrowBonus } from "../common/config/heroSet";
|
||||
import { HeroInfo, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, calcGrowBonus, HeroDisVal } from "../common/config/heroSet";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { SkillTriggerType } from "../common/config/heroSet";
|
||||
import { SkillTriggerType, HType } from "../common/config/heroSet";
|
||||
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||
import { Attrs } from "../common/config/HeroAttrs";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
@@ -76,8 +76,8 @@ export class Hero extends ecs.Entity {
|
||||
load(pos: Vec3 = Vec3.ZERO, scale: number = 1, uuid: number = 1001, dropToY: number = pos.y, hero_lv: number = 1, card_lv: number = 1, posIndex: number = -1) {
|
||||
// 英雄始终朝右,表现缩放固定为正向
|
||||
scale = 1
|
||||
// 英雄等级在当前规则下上限为 3,避免超配表范围
|
||||
if (hero_lv > 3) hero_lv = 3
|
||||
// 英雄等级上限以配置为准,合成可升到 HERO_MAX_LV
|
||||
if (hero_lv > FightSet.HERO_MAX_LV) hero_lv = FightSet.HERO_MAX_LV
|
||||
// 英雄尺寸随等级做轻量放大,强化成长反馈
|
||||
let size = 1.15
|
||||
// 根据配置路径加载英雄预制体
|
||||
@@ -120,8 +120,10 @@ export class Hero extends ecs.Entity {
|
||||
model.base_card_lv = hero.card_lv ?? 1;
|
||||
model.type = hero.type;
|
||||
model.grow_type = hero.grow_type;
|
||||
model.role = hero.role;
|
||||
model.fac = FacSet.HERO;
|
||||
model.dis = hero.dis ?? 720;
|
||||
// 攻击距离:配置优先;未配置按攻击定位取默认值(近战100 / 中程300 / 远程450)
|
||||
model.dis = hero.dis ?? HeroDisVal[hero.type as HType.Melee | HType.Mid | HType.Long];
|
||||
model.posIndex = posIndex;
|
||||
if (posIndex >= 0) {
|
||||
smc.mission.heroGrid[posIndex] = this.eid;
|
||||
@@ -131,7 +133,6 @@ export class Hero extends ecs.Entity {
|
||||
model.call = hero.call;
|
||||
model.dead = hero.dead;
|
||||
model.fstart = hero.fstart;
|
||||
model.fend = hero.fend;
|
||||
model.atking = hero.atking;
|
||||
model.atked = hero.atked;
|
||||
model.field = hero.field;
|
||||
@@ -155,7 +156,7 @@ export class Hero extends ecs.Entity {
|
||||
model.hp = model.hp_max = base_hp;
|
||||
}
|
||||
|
||||
model.speed = hero.speed ?? 800;
|
||||
model.speed = hero.speed ?? 200;
|
||||
|
||||
// 构建技能表并注入运行时冷却字段 ccd
|
||||
model.skills = {};
|
||||
@@ -170,7 +171,6 @@ export class Hero extends ecs.Entity {
|
||||
model.runtime_call = resolveTriggerByLv(model.call, hero_lv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, hero_lv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, hero_lv);
|
||||
model.runtime_fend = resolveTriggerByLv(model.fend, hero_lv);
|
||||
model.runtime_atking = resolveTriggerByLv(model.atking, hero_lv);
|
||||
model.runtime_atked = resolveTriggerByLv(model.atked, hero_lv);
|
||||
model.runtime_field = resolveFieldByLv(model.field, hero_lv);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { mLogger } from "../common/Logger";
|
||||
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||
import { getMonsterGoldDrop } from "../map/RogueConfig";
|
||||
import { MissionEconomy } from "../map/MissionEconomy";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
|
||||
/** 最终伤害数据接口
|
||||
* 用于封装一次攻击计算的所有结果数据
|
||||
@@ -146,15 +147,15 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
}
|
||||
}
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after crit", damage)
|
||||
// 护盾吸收
|
||||
// 护盾数值减免(1 点护盾吸收 1 点伤害,扣完为止,溢出部分继续扣血)
|
||||
const shieldResult = this.absorbShield(TAttrsComp, damage);
|
||||
damage = shieldResult.remainingDamage;
|
||||
if (shieldResult.absorbedDamage > 0) {
|
||||
// 【评分系统 - 防御分】统计护盾成功抵挡伤害的次数
|
||||
smc.vmdata.scores.shield_block_count += 1;
|
||||
// 【评分系统 - 防御分】统计护盾累计吸收的伤害量
|
||||
smc.vmdata.scores.shield_block_count += Math.floor(shieldResult.absorbedDamage);
|
||||
}
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield", damage)
|
||||
// 显示护盾格挡飘字
|
||||
// 显示护盾减免飘字
|
||||
if (shieldResult.absorbedDamage > 0 && targetView) {
|
||||
targetView.shield_tip(shieldResult.absorbedDamage);
|
||||
}
|
||||
@@ -175,11 +176,6 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
TAttrsComp.atked_count++;
|
||||
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
||||
|
||||
// 受击者产生击退效果
|
||||
// if (damage > 0 && targetView) {
|
||||
// targetView.back();
|
||||
// }
|
||||
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 英雄${TAttrsComp.hero_name} (uuid: ${TAttrsComp.hero_uuid}) 受到 eid:${casterEid} 的 伤害 ${damage},${isCrit ? "暴击" : "普通"}攻击,技能ID ${damageEvent.s_uuid}`);
|
||||
|
||||
// 冰冻判定
|
||||
@@ -190,14 +186,26 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
const stunChance = (damageEvent.Attrs[Attrs.stun_chance] || 0) - TAttrsComp.getFinalStunRes();
|
||||
const isStun = !TAttrsComp.isStun() && this.checkChance(stunChance);
|
||||
|
||||
// 击退判定
|
||||
const knockbackChance = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.knockback_res || 0);
|
||||
const isKnockback = this.checkChance(knockbackChance);
|
||||
// 击退:受击默认后退,击退属性不再作为概率,而是转换为后退距离的增量
|
||||
// 击退增量 = 施法者击退强化(原击退概率)- 被攻击者击退抗性 + 击退距离强化
|
||||
const knockbackBonus = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.getFinalKnockbackRes() || 0);
|
||||
const knockbackExtra = Math.max(0, knockbackBonus + (damageEvent.Attrs[Attrs.knockback_distance] || 0));
|
||||
|
||||
// ✅ 触发视图层表现(伤害数字、受击动画、冰冻、击晕、击退)
|
||||
if (targetView) {
|
||||
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, false);
|
||||
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, knockbackExtra);
|
||||
targetView.playEnd(skillConf.endAnm);
|
||||
|
||||
// 击退位移下沉到 MoveSystem:直接写入 knockbackVelocity,由 MoveSystem 每帧衰减并叠加位移
|
||||
const moveComp = target.get(MoveComp);
|
||||
if (moveComp) {
|
||||
const dist = FightSet.BACK_RANG + knockbackExtra;
|
||||
const isHero = TAttrsComp.fac === FacSet.HERO;
|
||||
// 击退初速度:0.1 秒时间常数衰减,总位移约等于 dist
|
||||
const knockbackSpeed = dist * 16;
|
||||
moveComp.knockbackVelocity = isHero ? -knockbackSpeed : knockbackSpeed;
|
||||
}
|
||||
|
||||
if (isFrost) {
|
||||
TAttrsComp.toFrost();
|
||||
targetView.in_iced(FightSet.FROST_TIME);
|
||||
@@ -212,18 +220,16 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
smc.vmdata.scores.stun_count++;
|
||||
}
|
||||
}
|
||||
if (isKnockback) {
|
||||
targetView.back(damageEvent.Attrs[Attrs.knockback_distance] || 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查死亡
|
||||
if (TAttrsComp.hp <= 0) {
|
||||
// 先触发死亡技能(如亡语),不管后续是否复活都应触发
|
||||
// 先触发死亡技能(如亡语),不管后续是否续命都应触发
|
||||
this.triggerDeadSkills(target);
|
||||
|
||||
// 复活机制:如果玩家属性内的复活属性值>=1 则执行复活,原地50%血量复活
|
||||
// 濒死回血(revive 系):不是真正复活,而是血量归零瞬间消耗 1 次续命次数,
|
||||
// 按比例回血继续战斗。次数每回合重置(见 MissionComp.healAllHeroes)。
|
||||
let canRevive = false;
|
||||
let maxReviveCount = 0;
|
||||
let reviveHpPercent = 50; // 默认恢复50%
|
||||
@@ -338,16 +344,16 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
|
||||
/**
|
||||
* 处理角色死亡
|
||||
*
|
||||
*
|
||||
* 死亡处理流程:
|
||||
* 1. 标记死亡状态(is_dead = true)
|
||||
* 2. 触发死亡事件(onDeath)
|
||||
* 3. 记录调试信息(如启用调试模式)
|
||||
*
|
||||
* 2. 英雄阵营:快照死亡状态到 smc.mission.dead_heroes(供后续复活机制重新召唤)
|
||||
* 3. 触发死亡事件(onDeath)
|
||||
*
|
||||
* @param entity 死亡的实体
|
||||
*
|
||||
*
|
||||
* @important 死亡状态一旦设置,该实体将不再处理新的伤害事件
|
||||
* 这确保了死亡逻辑的单一性和一致性
|
||||
* 英雄为真实死亡:视图动画结束后由 HeroViewComp.realDead 销毁实体
|
||||
*/
|
||||
private doDead(entity: ecs.Entity): void {
|
||||
const TAttrsComp = entity.get(HeroAttrsComp);
|
||||
@@ -355,6 +361,11 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
|
||||
TAttrsComp.is_dead = true;
|
||||
|
||||
// 英雄真实死亡:记录快照,供后续复活机制(重新召唤)消费
|
||||
if (TAttrsComp.fac === FacSet.HERO) {
|
||||
this.recordDeadHero(TAttrsComp);
|
||||
}
|
||||
|
||||
// 触发死亡事件
|
||||
this.onDeath(entity);
|
||||
|
||||
@@ -363,6 +374,24 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快照死亡英雄的静态/成长数据到 smc.mission.dead_heroes。
|
||||
* 仅记录重新召唤所需字段,不含战斗内临时状态(buff、技能 CD 等)。
|
||||
*
|
||||
* @param attrs 死亡英雄的属性组件
|
||||
*/
|
||||
private recordDeadHero(attrs: HeroAttrsComp): void {
|
||||
smc.mission.dead_heroes.push({
|
||||
uuid: attrs.hero_uuid,
|
||||
lv: attrs.lv,
|
||||
card_lv: attrs.card_lv,
|
||||
posIndex: attrs.posIndex,
|
||||
wave: Math.max(1, smc.vmdata?.mission_data?.level ?? 1),
|
||||
});
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem',
|
||||
` 死亡英雄已记录: ${attrs.hero_name}(uuid=${attrs.hero_uuid}) lv=${attrs.lv} pos=${attrs.posIndex} wave=${smc.vmdata?.mission_data?.level}, 当前共 ${smc.mission.dead_heroes.length} 个`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -371,7 +400,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
* 用于所有概率相关的判定:
|
||||
* - 暴击率判定
|
||||
* - 闪避率判定
|
||||
* - 击退概率判定
|
||||
* - 冰冻/击晕概率判定
|
||||
* - 其他特殊效果概率
|
||||
*
|
||||
* @param rate 概率值(0-100的百分比)
|
||||
@@ -396,29 +425,26 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
|
||||
|
||||
/**
|
||||
* 护盾吸收伤害
|
||||
*
|
||||
* 护盾吸收逻辑:
|
||||
* 1. 护盾值大于 0 时,抵挡本次伤害(免伤)
|
||||
* 2. 每次抵挡消耗 1 点护盾值
|
||||
* 3. 护盾归零时,重置护盾最大值属性
|
||||
*
|
||||
* 护盾数值减免伤害
|
||||
*
|
||||
* 护盾为数值减免型:
|
||||
* 1. 1 点护盾吸收 1 点伤害
|
||||
* 2. 护盾扣减实际吸收量,溢出部分继续结算到血量
|
||||
*
|
||||
* @param TAttrsComp 被攻击者的属性组件(包含当前护盾值)
|
||||
* @param damage 原始伤害值
|
||||
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和被格挡的伤害值
|
||||
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和被护盾减免的伤害值
|
||||
*/
|
||||
private absorbShield(TAttrsComp: HeroAttrsComp, damage: number): { remainingDamage: number, absorbedDamage: number } {
|
||||
if (TAttrsComp.shield <= 0) {
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " 护盾值小于等于0,无法吸收伤害");
|
||||
return { remainingDamage: damage, absorbedDamage: 0 };
|
||||
}
|
||||
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - 1);
|
||||
if (TAttrsComp.shield <= 0) {
|
||||
TAttrsComp.shield = 0;
|
||||
}
|
||||
const absorbed = Math.min(TAttrsComp.shield, damage);
|
||||
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - absorbed);
|
||||
TAttrsComp.dirty_shield = true;
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾抵挡1次伤害,剩余次数 ${TAttrsComp.shield}`);
|
||||
return { remainingDamage: 0, absorbedDamage: damage };
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾减免 ${absorbed.toFixed(1)} 点伤害,剩余护盾 ${TAttrsComp.shield.toFixed(1)}`);
|
||||
return { remainingDamage: damage - absorbed, absorbedDamage: absorbed };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,6 +494,11 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
if (TAttrsComp.fac === FacSet.MON) {
|
||||
// 怪物死亡处理
|
||||
this.scheduleDrop(entity);
|
||||
// oops.message 全局事件总线:怪物死亡(连杀计数等表现层消费)
|
||||
oops.message.dispatchEvent(GameEvent.MonDead, {
|
||||
isBoss: !!TAttrsComp.is_boss,
|
||||
worldPos: entity.get(HeroViewComp)?.node?.worldPosition?.clone() ?? null,
|
||||
});
|
||||
} else if (TAttrsComp.fac === FacSet.HERO) {
|
||||
// 英雄死亡处理
|
||||
this.scheduleHeroDeath(entity);
|
||||
@@ -495,6 +526,12 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
if (gold <= 0) return;
|
||||
|
||||
MissionEconomy.addCoin(gold);
|
||||
// oops.message 全局事件总线:金币飞行表现(账务已即时结算,飞行纯视觉)
|
||||
oops.message.dispatchEvent(GameEvent.CoinFly, {
|
||||
worldPos: entity.get(HeroViewComp)?.node?.worldPosition?.clone() ?? null,
|
||||
gold,
|
||||
isBoss: !!TAttrsComp.is_boss,
|
||||
});
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem',
|
||||
` ${TAttrsComp.hero_name} 死亡掉落金币 ${gold}(monType=${monType}, isBoss=${TAttrsComp.is_boss})`);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvReviveEntry, LvBonusEntry } from "../common/config/heroSet";
|
||||
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvReviveEntry, LvBonusEntry, FieldEntry, HRole } from "../common/config/heroSet";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
|
||||
@@ -29,6 +29,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
type: number = 0; // 0近战 1远程 2辅助
|
||||
fac: number = 0; // 0:hero 1:monster
|
||||
grow_type?: number; // 成长类型(HGrowType),驱动每级 hp/ap 线性成长
|
||||
role?: HRole; // 位置类型/职业定位(驱动编队站位优先级)
|
||||
// ==================== 基础属性(有初始值) ====================
|
||||
base_ap: number = 0; // 原始基础攻击(无任何加成)
|
||||
base_hp: number = 0; // 原始基础血量(无任何加成)
|
||||
@@ -46,7 +47,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
[SkillTriggerType.Call]?: TriggerGrouped;
|
||||
[SkillTriggerType.Dead]?: TriggerGrouped;
|
||||
[SkillTriggerType.FStart]?: TriggerGrouped;
|
||||
[SkillTriggerType.FEnd]?: TriggerGrouped;
|
||||
[SkillTriggerType.Atking]?: TriggerGrouped;
|
||||
[SkillTriggerType.Atked]?: TriggerGrouped;
|
||||
/** 驻场光环(按 lv 解锁,存活期间全局生效) */
|
||||
@@ -67,10 +67,8 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
/** 当前等级下生效的战前触发条目 */
|
||||
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
/** 当前等级下生效的战后触发条目 */
|
||||
runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
/** 当前等级下生效的驻场光环 uuid 列表 */
|
||||
runtime_field?: number[];
|
||||
/** 当前等级下生效的驻场光环词条列表 */
|
||||
runtime_field?: FieldEntry[];
|
||||
/** 当前等级下生效的复活配置 */
|
||||
runtime_revive?: { s_uuid: number; r_num: number; upr: number };
|
||||
|
||||
@@ -81,11 +79,12 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
freeze_res: number = 0; // 冰冻抗性
|
||||
stun_chance: number = 0; // 击晕概率
|
||||
stun_res: number = 0; // 击晕抗性
|
||||
knockback_chance: number = 0; // 击退概率
|
||||
knockback_chance: number = 0; // 击退强化(原击退概率,现转换为击退距离增量)
|
||||
knockback_distance: number = 0; // 击退距离强化
|
||||
knockback_res: number = 0; // 击退抗性
|
||||
knockback_res: number = 0; // 击退抗性(削减击退距离增量)
|
||||
crit_damage: number = 0; // 额外暴击伤害
|
||||
puncture_chance: number = 0; // 穿透概率
|
||||
puncture_dmg_bonus: number = 0; // 穿透后伤害增量(百分点,乘算加成)
|
||||
wfuny: number = 0; // 风怒
|
||||
|
||||
revived_count: number = 0; // 已复活次数
|
||||
@@ -151,11 +150,11 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
const addValue = Math.max(0, Math.floor(value));
|
||||
if (addValue <= 0) return;
|
||||
this.shield += addValue;
|
||||
this.shield = Math.min(this.shield, FightSet.SHIELD_MAX); // 限制护盾最大层数
|
||||
this.shield = Math.min(this.shield, FightSet.SHIELD_MAX); // 限制护盾值上限(数值减免型)
|
||||
if (this.shield < 0) this.shield = 0;
|
||||
this.dirty_shield = true; // 标记护盾需要更新
|
||||
if (this.debugMode) {
|
||||
mLogger.log(this.debugMode, 'HeroAttrs', ` 护盾次数变更: ${this.hero_name}, 变化=${addValue}, ${Math.floor(oldShield)} -> ${Math.floor(this.shield)}`);
|
||||
mLogger.log(this.debugMode, 'HeroAttrs', ` 护盾值变更: ${this.hero_name}, 变化=${addValue}, ${Math.floor(oldShield)} -> ${Math.floor(this.shield)}`);
|
||||
}
|
||||
}
|
||||
add_hp_max(value: number) {
|
||||
@@ -326,6 +325,12 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
return this.puncture_chance + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroPuncture);
|
||||
}
|
||||
|
||||
/** 英雄实时穿透伤害加成 = 基础加成 + 驻场加成。 */
|
||||
public getRuntimePunctureDmgBonus(): number {
|
||||
if (this.fac !== FacSet.HERO) return this.puncture_dmg_bonus;
|
||||
return this.puncture_dmg_bonus + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroPunctureDmg);
|
||||
}
|
||||
|
||||
/** 英雄实时暴击伤害 = 基础额外暴伤 + 驻场暴伤。 */
|
||||
public getRuntimeCritDamageBonus(): number {
|
||||
if (this.fac !== FacSet.HERO) return this.crit_damage;
|
||||
@@ -441,7 +446,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
return base + mods.flatSum + mods.pctSum;
|
||||
}
|
||||
|
||||
/** 最终击退概率 = 基础击退 + 限时修饰(加法模型) */
|
||||
/** 最终击退强化 = 基础击退强化 + 限时修饰(加法模型,转换为击退距离增量) */
|
||||
public getFinalKnockbackChance(): number {
|
||||
const mods = this.aggregateTimedMods(Attrs.knockback_chance);
|
||||
return this.knockback_chance + mods.flatSum + mods.pctSum;
|
||||
@@ -467,6 +472,13 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
return base + mods.flatSum + mods.pctSum;
|
||||
}
|
||||
|
||||
/** 最终穿透伤害加成 = 驻场实时加成 + 限时修饰(加法模型) */
|
||||
public getFinalPunctureDmgBonus(): number {
|
||||
const base = this.getRuntimePunctureDmgBonus();
|
||||
const mods = this.aggregateTimedMods(Attrs.puncture_dmg_bonus);
|
||||
return base + mods.flatSum + mods.pctSum;
|
||||
}
|
||||
|
||||
/** 最终风怒概率 = 驻场实时风怒 + 限时修饰(加法模型) */
|
||||
public getFinalWindFury(): number {
|
||||
const base = this.getRuntimeWindFury();
|
||||
@@ -549,6 +561,8 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.lv = 1;
|
||||
this.type = 0;
|
||||
this.fac = 0;
|
||||
this.grow_type = undefined;
|
||||
this.role = undefined;
|
||||
this.base_ap = 0;
|
||||
this.base_hp = 0;
|
||||
this.ap = 0;
|
||||
@@ -563,7 +577,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.call = undefined;
|
||||
this.dead = undefined;
|
||||
this.fstart = undefined;
|
||||
this.fend = undefined;
|
||||
this.atking = undefined;
|
||||
this.atked = undefined;
|
||||
this.field = undefined;
|
||||
@@ -575,7 +588,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.runtime_call = undefined;
|
||||
this.runtime_dead = undefined;
|
||||
this.runtime_fstart = undefined;
|
||||
this.runtime_fend = undefined;
|
||||
this.runtime_field = undefined;
|
||||
this.runtime_revive = undefined;
|
||||
this.critical = 0;
|
||||
@@ -591,6 +603,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.revived_count = 0;
|
||||
this.invincible_time = 0;
|
||||
this.puncture_chance = 0;
|
||||
this.puncture_dmg_bonus = 0;
|
||||
this.wfuny = 0;
|
||||
this.boom = false;
|
||||
|
||||
|
||||
173
assets/script/game/hero/HeroTopBarComp.ts
Normal file
173
assets/script/game/hero/HeroTopBarComp.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { _decorator, ProgressBar, Sprite, UIOpacity, Vec3, v3, tween, Tween, Color, clamp } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* 英雄头顶状态栏组件
|
||||
*
|
||||
* 挂载在英雄节点下的 "top" 子节点上,独立管理:
|
||||
* - 血条进度(ProgressBar)
|
||||
* - 护盾数值条(ProgressBar,top/shield 节点,显示 shield / SHIELD_MAX)
|
||||
* - 透明度激活 / 闲置
|
||||
* - 受击抖动动画
|
||||
*
|
||||
* 数据流:HeroViewComp 将 HeroAttrsComp 引用传入,本组件通过 model 读取属性。
|
||||
* 注意:英雄根节点下的 "shielded" 是护盾特效动画节点,由 HeroViewComp 管理,不在本组件职责内。
|
||||
*/
|
||||
@ccclass('HeroTopBarComp')
|
||||
@ecs.register('HeroTopBar', false)
|
||||
export class HeroTopBarComp extends CCComp {
|
||||
|
||||
// ==================== @property 绑定(编辑器拖拽) ====================
|
||||
@property({ type: ProgressBar, tooltip: "血条进度条" })
|
||||
private hpProgressBar: ProgressBar = null!;
|
||||
|
||||
@property({ type: ProgressBar, tooltip: "护盾数值条(top 下的 shield 节点)" })
|
||||
private shieldProgressBar: ProgressBar = null!;
|
||||
|
||||
@property({ type: Sprite, tooltip: "血条填充 Sprite(用于设置阵营颜色)" })
|
||||
private hpBarSprite: Sprite = null!;
|
||||
|
||||
// ==================== 内部引用 ====================
|
||||
private topOpacity: UIOpacity = null!;
|
||||
private basePos: Vec3 = v3();
|
||||
|
||||
// ==================== 配置 ====================
|
||||
private hp_height: number = 90;
|
||||
private boss_hp_height: number = 160;
|
||||
|
||||
// ==================== 透明度 ====================
|
||||
private readonly barIdleOpacity: number = 153;
|
||||
private readonly barActiveOpacity: number = 255;
|
||||
private readonly idleOpacityDelay: number = 0.25;
|
||||
|
||||
/** 外部赋值:英雄数据模型 */
|
||||
private _model: HeroAttrsComp = null!;
|
||||
public get model(): HeroAttrsComp { return this._model; }
|
||||
public set model(value: HeroAttrsComp) { this._model = value; }
|
||||
|
||||
private readonly restoreBarIdleOpacity = () => {
|
||||
this.setTopBarOpacity(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化 / 对象池复用时重置
|
||||
* @param model 英雄属性组件
|
||||
* @param direction 显示方向(1 或 -1)
|
||||
*/
|
||||
public init(model: HeroAttrsComp, direction: number = 1): void {
|
||||
this._model = model;
|
||||
|
||||
// UIOpacity 无 @property 绑定,运行时获取或添加
|
||||
this.topOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
||||
|
||||
// 方向取 abs,避免对象池复用时 scale 累乘导致血条反转
|
||||
this.node.setScale(direction * Math.abs(this.node.scale.x), 1 * this.node.scale.y);
|
||||
|
||||
// 位置
|
||||
const height = model.is_boss ? this.boss_hp_height : this.hp_height;
|
||||
this.node.setPosition(0, height, 0);
|
||||
this.basePos = this.node.position.clone();
|
||||
|
||||
// 血条颜色(英雄阵营为绿色)
|
||||
if (model.fac === FacSet.HERO && this.hpBarSprite) {
|
||||
this.hpBarSprite.color = new Color("#2ECC71");
|
||||
}
|
||||
|
||||
// 确保血条等 UI 始终在最上层
|
||||
this.node.setSiblingIndex(999);
|
||||
|
||||
// 重置显示状态
|
||||
this.node.getChildByName("hp")!.active = true;
|
||||
this.node.getChildByName("cd")!.active = false;
|
||||
this.node.getChildByName("lv")!.active = false;
|
||||
this.node.active = true;
|
||||
|
||||
this.setTopBarOpacity(false);
|
||||
this.hpShow();
|
||||
this.shieldShow(model.shield);
|
||||
}
|
||||
|
||||
/** 更新血量进度条 */
|
||||
public hpShow(): void {
|
||||
if (!this._model || !this.hpProgressBar) return;
|
||||
const hp = this._model.hp;
|
||||
const hpMax = this._model.hp_max;
|
||||
let targetProgress = hpMax > 0 ? hp / hpMax : 0;
|
||||
targetProgress = clamp(targetProgress, 0, 1);
|
||||
|
||||
const prevProgress = this.hpProgressBar.progress;
|
||||
if (targetProgress < prevProgress) {
|
||||
this.activateTopBar();
|
||||
this.playHpBarShake();
|
||||
}
|
||||
this.hpProgressBar.progress = targetProgress;
|
||||
if (targetProgress > prevProgress) {
|
||||
this.setTopBarOpacity(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 更新护盾数值条(百分比 = shield / SHIELD_MAX) */
|
||||
public shieldShow(shield: number = 0): void {
|
||||
if (!this.shieldProgressBar) return;
|
||||
const progress = FightSet.SHIELD_MAX > 0 ? shield / FightSet.SHIELD_MAX : 0;
|
||||
this.shieldProgressBar.progress = clamp(progress, 0, 1);
|
||||
// 护盾节点显隐
|
||||
this.shieldProgressBar.node.active = shield > 0;
|
||||
}
|
||||
|
||||
/** 激活顶栏(高亮 + 延迟恢复闲置透明度) */
|
||||
public activateTopBar(): void {
|
||||
this.setTopBarOpacity(true);
|
||||
this.unschedule(this.restoreBarIdleOpacity);
|
||||
this.scheduleOnce(this.restoreBarIdleOpacity, this.idleOpacityDelay);
|
||||
}
|
||||
|
||||
/** 设置顶栏透明度 */
|
||||
public setTopBarOpacity(isActive: boolean): void {
|
||||
if (!this.node || !this.node.isValid) return;
|
||||
if (this.topOpacity) {
|
||||
this.topOpacity.opacity = isActive ? this.barActiveOpacity : this.barIdleOpacity;
|
||||
}
|
||||
this.node.active = true;
|
||||
}
|
||||
|
||||
/** 显示顶栏 */
|
||||
public show(): void {
|
||||
if (this.node && this.node.isValid) this.node.active = true;
|
||||
}
|
||||
|
||||
/** 隐藏顶栏 */
|
||||
public hide(): void {
|
||||
if (this.node && this.node.isValid) this.node.active = false;
|
||||
}
|
||||
|
||||
/** 血条受击抖动动画 */
|
||||
private playHpBarShake(): void {
|
||||
if (!this.node || !this.node.isValid) return;
|
||||
Tween.stopAllByTarget(this.node);
|
||||
this.node.setPosition(this.basePos);
|
||||
tween(this.node)
|
||||
.by(0.04, { position: v3(-3, 0, 0) })
|
||||
.by(0.04, { position: v3(6, 0, 0) })
|
||||
.by(0.04, { position: v3(-5, 0, 0) })
|
||||
.by(0.04, { position: v3(2, 0, 0) })
|
||||
.call(() => {
|
||||
this.node.setPosition(this.basePos);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
/** 对象池回收 / 组件重置时调用 */
|
||||
public reset(): void {
|
||||
this.unscheduleAllCallbacks();
|
||||
if (this.node && this.node.isValid) {
|
||||
Tween.stopAllByTarget(this.node);
|
||||
this.node.setPosition(this.basePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/game/hero/HeroTopBarComp.ts.meta
Normal file
9
assets/script/game/hero/HeroTopBarComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c4181fc5-1168-4a6d-8d03-d0f2dd0b22a2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Vec3, _decorator, v3, Collider2D, Contact2DType, Label, Node, Prefab, instantiate, ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Tween, Color, BoxCollider2D, UITransform, UIOpacity } from "cc";
|
||||
import { Vec3, _decorator, v3, Collider2D, Contact2DType, Node, Prefab, instantiate, tween, Tween, BoxCollider2D, UITransform } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { HeroSpine } from "./HeroSpine";
|
||||
import { HeroTopBarComp } from "./HeroTopBarComp";
|
||||
import { BoxSet, FacSet, FightSet, NumberFormatter, TooltipTypes } from "../common/config/GameSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { SkillSet, } from "../common/config/SkillSet";
|
||||
@@ -37,21 +38,10 @@ export class HeroViewComp extends CCComp {
|
||||
realDeadTime: number = 0.1
|
||||
deadCD: number = 0
|
||||
monDeadTime: number = 0.1
|
||||
hp_height: number = 90
|
||||
boss_hp_height: number = 160
|
||||
// 血条显示相关
|
||||
lastBarUpdateTime: number = 0; // 最后一次血条/蓝条/护盾更新时间
|
||||
// ==================== UI 节点引用 ====================
|
||||
private top_node: Node = null!;
|
||||
private topOpacity: UIOpacity = null!;
|
||||
private topBasePos: Vec3 = v3();
|
||||
private lvLabel: Label | null = null; // 等级显示文本
|
||||
private readonly barIdleOpacity: number = 153;
|
||||
private readonly barActiveOpacity: number = 255;
|
||||
private readonly idleOpacityDelay: number = 0.25;
|
||||
private readonly restoreBarIdleOpacity = () => {
|
||||
this.setTopBarOpacity(false);
|
||||
};
|
||||
private topBar: HeroTopBarComp = null!;
|
||||
|
||||
// ==================== 直接访问 HeroAttrsComp ====================
|
||||
get model() {
|
||||
@@ -96,67 +86,26 @@ export class HeroViewComp extends CCComp {
|
||||
this.lastBarUpdateTime = 0;
|
||||
this.status_change("idle");
|
||||
|
||||
// 初始化 UI 节点
|
||||
this.initUINodes();
|
||||
// 初始化头顶状态栏
|
||||
const topNode = this.node.getChildByName("top");
|
||||
this.topBar = topNode.getComponent(HeroTopBarComp) || topNode.addComponent(HeroTopBarComp);
|
||||
this.topBar.init(this.model, this.scale);
|
||||
|
||||
/** 方向 */
|
||||
this.node.setScale(this.scale * Math.abs(this.node.scale.x), 1 * this.node.scale.y); // 确保 scale.x 为正后再乘方向
|
||||
// top_node 取 abs 避免对象池复用时 scale 累乘导致血条方向反转
|
||||
this.top_node.setScale(this.scale * Math.abs(this.top_node.scale.x), 1 * this.top_node.scale.y);
|
||||
/* 显示角色血*/
|
||||
this.top_node.getChildByName("hp").active = true;
|
||||
this.top_node.getChildByName("cd").active = false;
|
||||
this.top_node.getChildByName("shield").active = false;
|
||||
this.top_node.getChildByName("lv").active = true;
|
||||
this.top_node.active = true;
|
||||
this.setTopBarOpacity(false);
|
||||
|
||||
// 初始化等级显示
|
||||
this.lv_show();
|
||||
|
||||
// 🔥 重置血条 UI 显示状态
|
||||
// 🔥 重置描边
|
||||
if (this.model) {
|
||||
this.hp_show();
|
||||
|
||||
// 根据英雄模型中的等级数据设置描边
|
||||
// 注意:HeroViewComp 是挂在 node 上的,而 FlashSprite 可能挂在子节点(如 Sprite/anm 节点)
|
||||
let flashSprite = this.node.getComponent(FlashSprite);
|
||||
if (!flashSprite) {
|
||||
// 如果当前节点没有,尝试在所有子节点中查找
|
||||
flashSprite = this.node.getComponentInChildren(FlashSprite);
|
||||
}
|
||||
|
||||
if (flashSprite) {
|
||||
flashSprite.setOutlineByLevel(this.model.lv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 UI 节点引用 */
|
||||
private initUINodes() {
|
||||
this.top_node = this.node.getChildByName("top");
|
||||
this.topOpacity = this.top_node.getComponent(UIOpacity) || this.top_node.addComponent(UIOpacity);
|
||||
this.top_node.setPosition(0, this.hp_height, 0);
|
||||
if (this.model.is_boss) this.top_node.setPosition(0, this.boss_hp_height, 0);
|
||||
this.topBasePos = this.top_node.position.clone();
|
||||
const hpNode = this.top_node.getChildByName("hp");
|
||||
if (this.model.fac == FacSet.HERO) {
|
||||
hpNode.getChildByName("Bar").getComponent(Sprite).color = new Color("#2ECC71")
|
||||
}
|
||||
|
||||
// 确保血条等UI始终在最上层显示
|
||||
this.top_node.setSiblingIndex(999);
|
||||
|
||||
// 缓存等级显示 Label(lv 节点下的 Label 子节点)
|
||||
const lvNode = this.top_node.getChildByName("lv");
|
||||
if (lvNode) {
|
||||
this.lvLabel = lvNode.getChildByName("Label")?.getComponent(Label) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* View 层每帧更新
|
||||
* 注意:数据更新逻辑已移到 HeroAttrSystem,这里只负责显示
|
||||
@@ -181,103 +130,21 @@ export class HeroViewComp extends CCComp {
|
||||
|
||||
// ✅ 按需更新 UI(脏标签模式)- 只在属性变化时更新
|
||||
if (this.model.dirty_hp) {
|
||||
this.hp_show();
|
||||
this.topBar.hpShow();
|
||||
this.model.dirty_hp = false;
|
||||
}
|
||||
|
||||
|
||||
if (this.model.dirty_shield) {
|
||||
this.show_shield(this.model.shield);
|
||||
this.topBar.shieldShow(this.model.shield);
|
||||
this.model.dirty_shield = false;
|
||||
}
|
||||
|
||||
if (this.model.dirty_lv) {
|
||||
this.lv_show();
|
||||
this.model.dirty_lv = false;
|
||||
}
|
||||
}
|
||||
|
||||
public cd_show() {
|
||||
return;
|
||||
}
|
||||
|
||||
/** 显示护盾 */
|
||||
private show_shield(shield: number = 0) {
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
this.node.getChildByName("shielded").active = shield > 0;
|
||||
}
|
||||
|
||||
/** 显示血量 */
|
||||
private hp_show() {
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
// 不再基于血量是否满来决定显示状态,只更新进度条
|
||||
let hp = this.model.hp;
|
||||
let hp_max = this.model.hp_max;
|
||||
// mLogger.log(this.debugMode, 'HeroViewComp', "hp_show",hp,hp_max)
|
||||
|
||||
let targetProgress = hp_max > 0 ? hp / hp_max : 0;
|
||||
targetProgress = clamp(targetProgress, 0, 1);
|
||||
let hpNode = this.top_node.getChildByName("hp");
|
||||
let hpProgressBar = hpNode.getComponent(ProgressBar);
|
||||
|
||||
const prevProgress = hpProgressBar.progress;
|
||||
if (targetProgress < hpProgressBar.progress) {
|
||||
this.activateTopBar();
|
||||
this.playHpBarShake();
|
||||
}
|
||||
hpProgressBar.progress = targetProgress;
|
||||
if (targetProgress > prevProgress) {
|
||||
this.setTopBarOpacity(false);
|
||||
}
|
||||
}
|
||||
|
||||
private isFullHpAndNoShield(): boolean {
|
||||
if (!this.model) return false;
|
||||
if (this.model.hp_max <= 0) return false;
|
||||
return this.model.hp >= this.model.hp_max;
|
||||
}
|
||||
|
||||
/** 更新等级显示 */
|
||||
private lv_show() {
|
||||
if (!this.lvLabel || !this.model) return;
|
||||
this.lvLabel.string = String(this.model.lv);
|
||||
}
|
||||
|
||||
private setTopBarOpacity(isActive: boolean) {
|
||||
if (!this.top_node || !this.top_node.isValid) return;
|
||||
if (this.topOpacity) {
|
||||
this.topOpacity.opacity = this.barActiveOpacity;
|
||||
}
|
||||
if (isActive) {
|
||||
this.top_node.active = true;
|
||||
return;
|
||||
}
|
||||
this.top_node.active = true;
|
||||
}
|
||||
|
||||
private activateTopBar() {
|
||||
this.setTopBarOpacity(true);
|
||||
this.unschedule(this.restoreBarIdleOpacity);
|
||||
this.scheduleOnce(this.restoreBarIdleOpacity, this.idleOpacityDelay);
|
||||
}
|
||||
|
||||
private playHpBarShake() {
|
||||
if (!this.top_node || !this.top_node.isValid) return;
|
||||
Tween.stopAllByTarget(this.top_node);
|
||||
this.top_node.setPosition(this.topBasePos);
|
||||
tween(this.top_node)
|
||||
.by(0.04, { position: v3(-3, 0, 0) })
|
||||
.by(0.04, { position: v3(6, 0, 0) })
|
||||
.by(0.04, { position: v3(-5, 0, 0) })
|
||||
.by(0.04, { position: v3(2, 0, 0) })
|
||||
.call(() => {
|
||||
this.top_node.setPosition(this.topBasePos);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 升级特效 */
|
||||
public lv_up() {
|
||||
this.spawnTimedFx("game/skill/buff/buff_lvup", this.node, 1.0);
|
||||
@@ -349,7 +216,7 @@ export class HeroViewComp extends CCComp {
|
||||
Tooltip.load(pos, type, value, s_uuid, this.node.parent, 1, this.model?.fac ?? FacSet.MON);
|
||||
}
|
||||
|
||||
/** 护盾格挡提示 */
|
||||
/** 护盾减免提示 */
|
||||
shield_tip(absorbed: number) {
|
||||
this.hp_tip(TooltipTypes.shield, NumberFormatter.formatNumber(Math.max(0, Math.floor(absorbed))));
|
||||
}
|
||||
@@ -432,7 +299,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
add_shield(shield: number) {
|
||||
// 护盾数据更新由 Model 层处理,这里只负责视图表现
|
||||
if (this.model && this.model.shield > 0) this.show_shield(this.model.shield);
|
||||
if (this.model && this.model.shield > 0) this.topBar.shieldShow(this.model.shield);
|
||||
}
|
||||
|
||||
health(hp: number = 0) {
|
||||
@@ -458,7 +325,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
|
||||
// 恢复UI
|
||||
this.top_node.active = true;
|
||||
this.topBar.show();
|
||||
|
||||
this.status_change("idle");
|
||||
|
||||
@@ -504,7 +371,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
|
||||
// 隐藏UI
|
||||
this.top_node.active = false;
|
||||
this.topBar.hide();
|
||||
|
||||
// 在销毁实体前先禁用碰撞体,从源头减少"尸体"参与碰撞
|
||||
const collider = this.node.getComponent(Collider2D);
|
||||
@@ -516,23 +383,19 @@ export class HeroViewComp extends CCComp {
|
||||
let isHero = this.model.fac === FacSet.HERO;
|
||||
let dirX = isHero ? -800 : 800;
|
||||
|
||||
// 死亡往后飞出屏幕动画
|
||||
// 死亡往后飞出屏幕动画,结束后统一销毁实体
|
||||
// 英雄为真实死亡:快照已在 doDead 阶段写入 smc.mission.dead_heroes,此处直接销毁
|
||||
tween(this.node)
|
||||
.by(0.5, { position: v3(dirX, 700, 0) }, { easing: "quadOut" })
|
||||
.call(() => {
|
||||
if (isHero) {
|
||||
// 将英雄移到玩家看不到的墓地,留待下回合上场
|
||||
this.node.setPosition(v3(-2000, -2000, 0));
|
||||
} else {
|
||||
// 动画结束后销毁怪物实体
|
||||
this.ent.destroy();
|
||||
}
|
||||
this.ent.destroy();
|
||||
})
|
||||
.start();
|
||||
}
|
||||
do_atked(damage: number, isCrit: boolean, s_uuid: number, isBack: boolean = false) {
|
||||
/** 受击表现(伤害数字、受击动画、击退) */
|
||||
do_atked(damage: number, isCrit: boolean, s_uuid: number, knockbackExtra: number = 0) {
|
||||
// 受到攻击时更新最后更新时间
|
||||
this.activateTopBar();
|
||||
this.topBar.activateTopBar();
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
|
||||
if (damage <= 0) return;
|
||||
@@ -547,43 +410,15 @@ export class HeroViewComp extends CCComp {
|
||||
// 视图层表现
|
||||
let SConf = SkillSet[s_uuid]
|
||||
const hitAnm = SConf?.DAnm || "atked";
|
||||
if (isBack) this.back()
|
||||
this.back(knockbackExtra)
|
||||
this.in_atked(hitAnm, this.model.fac == FacSet.HERO ? 1 : -1);
|
||||
this.showDamage(damage, isCrit);
|
||||
}
|
||||
|
||||
private isBackingUp: boolean = false; // 🔥 添加后退状态标记
|
||||
|
||||
//后退
|
||||
back(distance: number = 0) {
|
||||
// 🔥 防止重复调用后退动画
|
||||
if (this.isBackingUp) return;
|
||||
this.isBackingUp = true; // 🔥 设置后退状态
|
||||
|
||||
if (this.model.fac == FacSet.MON) {
|
||||
// 基础击退距离,加上额外的距离强化
|
||||
let dist = FightSet.BACK_RANG + distance;
|
||||
let tx = this.node.position.x + dist;
|
||||
if (tx > 320) tx = 320;
|
||||
tween(this.node)
|
||||
.to(0.1, { position: v3(tx, this.node.position.y, 0) })
|
||||
.call(() => {
|
||||
this.isBackingUp = false; // 🔥 动画完成后重置状态
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
if (this.model.fac == FacSet.HERO) {
|
||||
let dist = 5 + distance;
|
||||
let tx = this.node.position.x - dist;
|
||||
if (tx < -320) tx = -320;
|
||||
tween(this.node)
|
||||
.to(0.1, { position: v3(tx, this.node.position.y, 0) })
|
||||
.call(() => {
|
||||
this.isBackingUp = false; // 🔥 动画完成后重置状态
|
||||
})
|
||||
.start();
|
||||
}
|
||||
// 击退位移由 MoveSystem 统一处理,视图层只负责触发,不直接操作 position
|
||||
// 具体击退速度由 HeroAtkSystem 在伤害结算后写入 MoveComp.knockbackVelocity
|
||||
}
|
||||
// 伤害计算和战斗逻辑已迁移到 HeroBattleSystem
|
||||
|
||||
@@ -634,7 +469,7 @@ export class HeroViewComp extends CCComp {
|
||||
if (!this.model) return;
|
||||
|
||||
const damageText = NumberFormatter.formatNumber(Math.max(0, Math.floor(damage)));
|
||||
this.hp_show();
|
||||
this.topBar.hpShow();
|
||||
if (isCrit) {
|
||||
this.hp_tip(TooltipTypes.crit, damageText);
|
||||
} else {
|
||||
@@ -645,10 +480,7 @@ export class HeroViewComp extends CCComp {
|
||||
// 清理残留的定时器和缓动
|
||||
this.unscheduleAllCallbacks();
|
||||
Tween.stopAllByTarget(this.node);
|
||||
if (this.top_node && this.top_node.isValid) {
|
||||
Tween.stopAllByTarget(this.top_node);
|
||||
this.top_node.setPosition(this.topBasePos);
|
||||
}
|
||||
this.topBar.reset();
|
||||
|
||||
// 清理碰撞器事件监听
|
||||
const collider = this.getComponent(Collider2D);
|
||||
@@ -657,7 +489,6 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
this.deadCD = 0
|
||||
this.lastBarUpdateTime = 0
|
||||
this.unschedule(this.restoreBarIdleOpacity);
|
||||
|
||||
// 清理伤害队列
|
||||
this.damageQueue.length = 0;
|
||||
|
||||
@@ -3,13 +3,12 @@ import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/O
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
|
||||
import { HeroInfo, resolveTriggerByLv, resolveReviveByLv } from "../common/config/heroSet";
|
||||
import { HeroInfo, resolveTriggerByLv, resolveReviveByLv, HeroDisVal } from "../common/config/heroSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
import { MonMoveComp } from "./MonMoveComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { SkillTriggerType } from "../common/config/heroSet";
|
||||
import { SkillTriggerType, HType } from "../common/config/heroSet";
|
||||
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||
/** 怪物实体:负责怪物对象池复用、属性初始化、入场动画与回收 */
|
||||
@ecs.register(`Monster`)
|
||||
@@ -19,7 +18,7 @@ export class Monster extends ecs.Entity {
|
||||
/** 怪物表现组件引用 */
|
||||
HeroView!: HeroViewComp;
|
||||
/** 怪物移动组件引用 */
|
||||
MonMove!: MonMoveComp;
|
||||
MonMove!: MoveComp;
|
||||
/** 调试开关,控制生命周期日志输出 */
|
||||
private debugMode: boolean = false;
|
||||
|
||||
@@ -98,7 +97,7 @@ export class Monster extends ecs.Entity {
|
||||
/** 注册实体必需组件:移动 + 属性 */
|
||||
protected init() {
|
||||
this.addComponents<ecs.Comp>(
|
||||
MonMoveComp,
|
||||
MoveComp,
|
||||
HeroAttrsComp,
|
||||
);
|
||||
}
|
||||
@@ -149,8 +148,9 @@ export class Monster extends ecs.Entity {
|
||||
(node as any)._baseScale = node.scale.clone();
|
||||
}
|
||||
|
||||
// 统一挂到实体显示层 HERO 节点下
|
||||
node.parent = scene.entityLayer!.node!.getChildByName("HERO")!;
|
||||
// 按身份挂载显示层:Boss → BOSS 层(最底),小怪 → MON 层(编辑器已配置好层级顺序)
|
||||
const layerName = is_boss ? "BOSS" : "MON";
|
||||
node.parent = scene.entityLayer!.node!.getChildByName(layerName)!;
|
||||
var view = node.getComponent(HeroViewComp)!;
|
||||
const collider = node.getComponent(BoxCollider2D);
|
||||
// 入场期间关闭碰撞,防止下落时提前参与战斗
|
||||
@@ -175,7 +175,8 @@ export class Monster extends ecs.Entity {
|
||||
model.speed = hero.speed ?? 800;
|
||||
model.type = hero.type;
|
||||
model.fac = FacSet.MON;
|
||||
model.dis = hero.dis ?? 720;
|
||||
// 攻击距离:配置优先;未配置按攻击定位取默认值(近战100 / 中程300 / 远程450)
|
||||
model.dis = hero.dis ?? HeroDisVal[hero.type as HType.Melee | HType.Mid | HType.Long];
|
||||
model.posIndex = laneIndex;
|
||||
if (laneIndex >= 0) {
|
||||
smc.mission.monGrid[laneIndex] = this.eid;
|
||||
@@ -185,7 +186,6 @@ export class Monster extends ecs.Entity {
|
||||
model.call = hero.call;
|
||||
model.dead = hero.dead;
|
||||
model.fstart = hero.fstart;
|
||||
model.fend = hero.fend;
|
||||
model.atking = hero.atking;
|
||||
model.atked = hero.atked;
|
||||
model.revive = hero.revive;
|
||||
@@ -224,7 +224,6 @@ export class Monster extends ecs.Entity {
|
||||
if (testSkills.atked !== undefined) model.atked = testSkills.atked;
|
||||
if (testSkills.dead !== undefined) model.dead = testSkills.dead;
|
||||
if (testSkills.fstart !== undefined) model.fstart = testSkills.fstart;
|
||||
if (testSkills.fend !== undefined) model.fend = testSkills.fend;
|
||||
if (testSkills.call !== undefined) model.call = testSkills.call;
|
||||
if (testSkills.revive !== undefined) model.revive = testSkills.revive;
|
||||
}
|
||||
@@ -233,7 +232,6 @@ export class Monster extends ecs.Entity {
|
||||
model.runtime_call = resolveTriggerByLv(model.call, mon_lv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, mon_lv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, mon_lv);
|
||||
model.runtime_fend = resolveTriggerByLv(model.fend, mon_lv);
|
||||
model.runtime_atking = resolveTriggerByLv(model.atking, mon_lv);
|
||||
model.runtime_atked = resolveTriggerByLv(model.atked, mon_lv);
|
||||
model.runtime_revive = resolveReviveByLv(model.revive, mon_lv);
|
||||
@@ -248,7 +246,7 @@ export class Monster extends ecs.Entity {
|
||||
// 广播怪物加载事件,供刷怪与战斗系统联动
|
||||
oops.message.dispatchEvent("monster_load", this)
|
||||
// 初始化移动参数:方向、目标 X、站位基准 Y
|
||||
const move = this.get(MonMoveComp);
|
||||
const move = this.get(MoveComp);
|
||||
move.reset();
|
||||
move.direction = -1;
|
||||
move.targetX = pos.x;
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { BoxSet, FacSet } from "../common/config/GameSet";
|
||||
import { Node } from "cc";
|
||||
|
||||
@ecs.register('MonMoveComp')
|
||||
export class MonMoveComp extends ecs.Comp {
|
||||
/** 朝向:1=向右,-1=向左 */
|
||||
direction: number = -1;
|
||||
/** 当前移动目标 X */
|
||||
targetX: number = 0;
|
||||
/** 是否允许移动(出生落地前会短暂关闭) */
|
||||
moving: boolean = true;
|
||||
/** 站位基准 Y */
|
||||
baseY: number = 0;
|
||||
/** 出生序,用于同条件渲染排序稳定 */
|
||||
spawnOrder: number = 0;
|
||||
|
||||
reset() {
|
||||
this.direction = -1;
|
||||
this.targetX = 0;
|
||||
this.moving = true;
|
||||
this.baseY = 0;
|
||||
this.spawnOrder = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ecs.register('MonMoveSystem')
|
||||
export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||
/** 怪物接近英雄后的停船距离:小于该阈值即停下交战 */
|
||||
private readonly combatHaltDistance = 80;
|
||||
/** 渲染层级重排节流,避免每帧排序 */
|
||||
private readonly renderSortInterval = 0.05;
|
||||
private lastRenderSortAt = 0;
|
||||
private monMoveMatcher: ecs.IMatcher | null = null;
|
||||
private heroViewMatcher: ecs.IMatcher | null = null;
|
||||
private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number }[] = [];
|
||||
private renderEntryCount = 0;
|
||||
|
||||
private getMonMoveMatcher(): ecs.IMatcher {
|
||||
if (!this.monMoveMatcher) {
|
||||
this.monMoveMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp, MonMoveComp);
|
||||
}
|
||||
return this.monMoveMatcher;
|
||||
}
|
||||
|
||||
private getHeroViewMatcher(): ecs.IMatcher {
|
||||
if (!this.heroViewMatcher) {
|
||||
this.heroViewMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp);
|
||||
}
|
||||
return this.heroViewMatcher;
|
||||
}
|
||||
|
||||
filter(): ecs.IMatcher {
|
||||
return ecs.allOf(MonMoveComp, HeroViewComp, HeroAttrsComp);
|
||||
}
|
||||
|
||||
update(e: ecs.Entity) {
|
||||
/** 战斗未开始/暂停时不驱动移动 */
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
|
||||
const model = e.get(HeroAttrsComp);
|
||||
const move = e.get(MonMoveComp);
|
||||
const view = e.get(HeroViewComp);
|
||||
if (!model || !move || !view || !view.node) return;
|
||||
if (model.fac !== FacSet.MON) return;
|
||||
if (!move.moving) return;
|
||||
|
||||
/** 关卡阶段性冻结怪物行为 */
|
||||
if (smc.mission.stop_mon_action) {
|
||||
this.clearCombatTarget(model);
|
||||
view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost() || model.isStun()) {
|
||||
this.clearCombatTarget(model);
|
||||
if (!model.is_reviving) view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
/** 所有移动都锁定在 baseY,避免出现“漂移” */
|
||||
if (view.node.position.y !== move.baseY) {
|
||||
view.node.setPosition(view.node.position.x, move.baseY, 0);
|
||||
}
|
||||
// 渲染层级统交由 MoveSystem 统一处理,避免两个 System 争抢 setSiblingIndex
|
||||
|
||||
// 仅在战斗中才处理索敌;非战斗阶段也允许向左推进,让怪物自然逼近英雄阵地
|
||||
const nearestEnemy = smc.mission.in_fight ? this.findNearestEnemy(e) : null;
|
||||
if (nearestEnemy) {
|
||||
/** 有敌人:边移动边攻击 */
|
||||
this.processCombatLogic(e, move, view, model, nearestEnemy);
|
||||
this.syncCombatTarget(model, view, nearestEnemy);
|
||||
} else {
|
||||
/** 无敌人:清目标并继续向左推进 */
|
||||
this.clearCombatTarget(model);
|
||||
model.is_atking = false;
|
||||
this.moveLeft(view, move, model);
|
||||
}
|
||||
}
|
||||
|
||||
private clearCombatTarget(model: HeroAttrsComp): void {
|
||||
model.combat_target_eid = -1;
|
||||
model.enemy_in_cast_range = false;
|
||||
}
|
||||
|
||||
private syncCombatTarget(model: HeroAttrsComp, selfView: HeroViewComp, enemyView: HeroViewComp): void {
|
||||
if (!enemyView || !enemyView.node || !enemyView.ent) {
|
||||
this.clearCombatTarget(model);
|
||||
return;
|
||||
}
|
||||
const enemyAttrs = enemyView.ent.get(HeroAttrsComp);
|
||||
if (!enemyAttrs || enemyAttrs.is_dead || enemyAttrs.is_reviving || enemyAttrs.fac === model.fac) {
|
||||
this.clearCombatTarget(model);
|
||||
return;
|
||||
}
|
||||
model.combat_target_eid = enemyView.ent.eid;
|
||||
model.enemy_in_cast_range = this.isEnemyInAttackRange(model, selfView.node.position.x, enemyView.node.position.x);
|
||||
}
|
||||
|
||||
private isEnemyInAttackRange(model: HeroAttrsComp, selfX: number, enemyX: number): boolean {
|
||||
const dist = Math.abs(selfX - enemyX);
|
||||
const attackRange = model.dis;
|
||||
return dist <= attackRange;
|
||||
}
|
||||
|
||||
private processCombatLogic(e: ecs.Entity, move: MonMoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
const selfX = view.node.position.x;
|
||||
const enemyX = enemy.node.position.x;
|
||||
// 停船判定使用固定阈值(80),不依赖技能攻击范围
|
||||
const inRange = Math.abs(selfX - enemyX) <= this.combatHaltDistance;
|
||||
|
||||
if (inRange) {
|
||||
// 接触到英雄方:停止移动,进入攻击状态,由 SCastSystem 负责技能释放
|
||||
model.is_atking = true;
|
||||
const dir = enemyX > selfX ? 1 : -1;
|
||||
view.scale = dir;
|
||||
if (view.status === "move") {
|
||||
view.status_change("idle");
|
||||
}
|
||||
} else {
|
||||
// 未接触:继续向左推进
|
||||
model.is_atking = false;
|
||||
this.moveLeft(view, move, model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 持续向左推进:怪物不再固定站位,而是以 speed/3 的速度向左移动,
|
||||
* 直到抵达左边界(基地位置)后转入待机,由 SCastSystem 继续负责技能释放。
|
||||
*/
|
||||
private moveLeft(view: HeroViewComp, move: MonMoveComp, model: HeroAttrsComp) {
|
||||
const currentX = view.node.position.x;
|
||||
if (currentX <= BoxSet.LETF_END) {
|
||||
if (view.status !== "atk") {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const dir = -1;
|
||||
move.direction = dir;
|
||||
const speed = model.speed / 3;
|
||||
const delta = speed * this.dt * dir;
|
||||
const newX = Math.max(BoxSet.LETF_END, currentX + delta);
|
||||
if (Math.abs(newX - currentX) >= 0.01) {
|
||||
view.node.setPosition(newX, view.node.position.y, 0);
|
||||
view.status_change("move");
|
||||
}
|
||||
}
|
||||
|
||||
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
|
||||
const currentView = entity.get(HeroViewComp);
|
||||
if (!currentView?.node) return null;
|
||||
|
||||
const currentPos = currentView.node.position;
|
||||
const myFac = entity.get(HeroAttrsComp).fac;
|
||||
|
||||
let nearest: HeroViewComp | null = null;
|
||||
let minDis = Infinity;
|
||||
|
||||
/** 遍历筛出最近敌人:以 X 轴距离为主,Y 轴距离作为同排的决胜权重,使角色优先攻击同路的敌人 */
|
||||
ecs.query(this.getHeroViewMatcher()).forEach(e => {
|
||||
const m = e.get(HeroAttrsComp);
|
||||
if (m.fac !== myFac && !m.is_dead) {
|
||||
const v = e.get(HeroViewComp);
|
||||
if (v?.node) {
|
||||
const dx = Math.abs(currentPos.x - v.node.position.x);
|
||||
const dy = Math.abs(currentPos.y - v.node.position.y);
|
||||
const d = dx + dy * 0.1; // Y轴权重较小,仅在 X 相近时起决定作用
|
||||
if (d < minDis) {
|
||||
minDis = d;
|
||||
nearest = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return nearest;
|
||||
}
|
||||
}
|
||||
@@ -1,97 +1,55 @@
|
||||
/**
|
||||
* @file MoveComp.ts
|
||||
* @description 统一移动组件与系统(英雄 + 怪物)
|
||||
*
|
||||
* 核心规则:
|
||||
* 1. 准备阶段:双方原地待命,不推进不索敌。
|
||||
* 2. 战斗阶段:双方向最近敌人推进,进入攻击范围(model.dis)后停止移动并攻击。
|
||||
* 3. 场上无敌人:停止移动(战斗结束,由 MissionComp 进入结算)。
|
||||
* 4. 击退/位移不会把单位推出屏幕:HERO 不越过 BoxSet.LETF_END,MON 不越过 BoxSet.RIGHT_END。
|
||||
* 5. 推进方向无上限限制,直到接触敌人或抵达敌方边界。
|
||||
*/
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { BoxSet, FacSet } from "../common/config/GameSet";
|
||||
import { HeroDisVal, HType } from "../common/config/heroSet";
|
||||
import { BoxCollider2D, Node } from "cc";
|
||||
import { MonMoveComp } from "./MonMoveComp";
|
||||
import { Node } from "cc";
|
||||
|
||||
@ecs.register('MoveComp')
|
||||
export class MoveComp extends ecs.Comp {
|
||||
/** 朝向:1=向右,-1=向左 */
|
||||
/** 朝向:1=向右(HERO),-1=向左(MON),由系统根据 fac 自动维护 */
|
||||
direction: number = 1;
|
||||
/** 当前移动目标 X(战斗/回位都会更新) */
|
||||
/** 当前移动目标 X */
|
||||
targetX: number = 0;
|
||||
/** 是否允许移动(出生落地前会短暂关闭) */
|
||||
moving: boolean = true;
|
||||
/** 预留:目标 Y(当前逻辑主要使用 baseY 锁定地平线) */
|
||||
targetY: number = 0;
|
||||
/** 角色所属车道的地平线 Y,移动时强制贴地 */
|
||||
/** 站位基准 Y,移动时强制贴地 */
|
||||
baseY: number = 0;
|
||||
/** 预留:车道索引 */
|
||||
lane: number = 0;
|
||||
/** 出生序,用于同条件渲染排序稳定 */
|
||||
spawnOrder: number = 0;
|
||||
/** 击退速度(像素/秒),受击瞬间由 HeroAtkSystem 赋值,MoveSystem 每帧指数衰减至 0 */
|
||||
knockbackVelocity: number = 0;
|
||||
|
||||
reset() {
|
||||
this.direction = 1;
|
||||
this.targetX = 0;
|
||||
this.moving = true;
|
||||
this.targetY = 0;
|
||||
this.baseY = 0;
|
||||
this.lane = 0;
|
||||
this.spawnOrder = 0;
|
||||
this.knockbackVelocity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface MoveFacConfig {
|
||||
/** 阵营可前进边界(靠近敌方一侧) */
|
||||
moveFrontX: number;
|
||||
/** 阵营可后退边界(靠近己方一侧) */
|
||||
moveBackX: number;
|
||||
/** 被逼退时前侧安全边界 */
|
||||
retreatFrontX: number;
|
||||
/** 被逼退时后侧安全边界 */
|
||||
retreatBackX: number;
|
||||
}
|
||||
|
||||
import { MissionHeroComp } from "../map/MissionHeroComp";
|
||||
|
||||
@ecs.register('MoveSystem')
|
||||
export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||
private readonly heroFrontAnchorX = -200;
|
||||
private readonly monFrontAnchorX = 0;
|
||||
/** 常规同阵营横向最小间距(英雄) */
|
||||
private readonly heroAllySpacingX = 100;
|
||||
/** 常规同阵营横向最小间距(怪物) */
|
||||
private readonly monAllySpacingX = 75;
|
||||
/** 纵向判定为同排的最大 Y 差 */
|
||||
private readonly minSpacingY = 30;
|
||||
/** 渲染层级重排节流,避免每帧排序 */
|
||||
private readonly renderSortInterval = 0.05;
|
||||
private lastRenderSortAt = 0;
|
||||
private heroMoveMatcher: ecs.IMatcher | null = null;
|
||||
private heroViewMatcher: ecs.IMatcher | null = null;
|
||||
private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number; laneScore: number }[] = [];
|
||||
private renderEntryCount = 0;
|
||||
|
||||
/**
|
||||
* 阵营可移动边界配置。
|
||||
* HERO 在左侧出生并向右推进;MON 在右侧出生并向左推进。
|
||||
*/
|
||||
private readonly facConfigs: Record<number, MoveFacConfig> = {
|
||||
[FacSet.HERO]: {
|
||||
moveFrontX: 999999,
|
||||
moveBackX: -999999,
|
||||
retreatFrontX: 999999,
|
||||
retreatBackX: -999999,
|
||||
},
|
||||
[FacSet.MON]: {
|
||||
moveFrontX: -999999,
|
||||
moveBackX: 999999,
|
||||
retreatFrontX: -999999,
|
||||
retreatBackX: 999999,
|
||||
}
|
||||
};
|
||||
|
||||
private getHeroMoveMatcher(): ecs.IMatcher {
|
||||
if (!this.heroMoveMatcher) {
|
||||
this.heroMoveMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp, MoveComp);
|
||||
}
|
||||
return this.heroMoveMatcher;
|
||||
}
|
||||
|
||||
private getHeroViewMatcher(): ecs.IMatcher {
|
||||
if (!this.heroViewMatcher) {
|
||||
this.heroViewMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp);
|
||||
@@ -106,75 +64,68 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
update(e: ecs.Entity) {
|
||||
/** 战斗未开始/暂停时不驱动移动 */
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
|
||||
|
||||
const model = e.get(HeroAttrsComp);
|
||||
const move = e.get(MoveComp);
|
||||
const view = e.get(HeroViewComp);
|
||||
if (!model || !move || !view || !view.node) return;
|
||||
if (model.fac !== FacSet.HERO) return; // 只处理英雄移动
|
||||
if (!move.moving) return;
|
||||
|
||||
if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost()) {
|
||||
/** 关卡阶段性冻结怪物行为 */
|
||||
if (smc.mission.stop_mon_action && model.fac === FacSet.MON) {
|
||||
this.clearCombatTarget(model);
|
||||
view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
/** 死亡/复活/控制状态:停止移动与索敌 */
|
||||
if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost() || model.isStun()) {
|
||||
this.clearCombatTarget(model);
|
||||
if (!model.is_reviving) view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 获取全局排位目标
|
||||
const slot = this.getGlobalFormationSlot(e, model);
|
||||
move.baseY = slot.targetY;
|
||||
move.targetX = slot.targetX;
|
||||
/** 所有移动都锁定在 baseY,避免出现"漂移" */
|
||||
if (view.node.position.y !== move.baseY) {
|
||||
view.node.setPosition(view.node.position.x, move.baseY, 0);
|
||||
}
|
||||
|
||||
// 战斗阶段不移动
|
||||
const canMove = !smc.mission.in_fight;
|
||||
|
||||
// 2. 平滑 Y 轴换路
|
||||
let isChangingLane = false;
|
||||
if (canMove) {
|
||||
if (Math.abs(view.node.position.y - move.baseY) > 2) {
|
||||
const currentY = view.node.position.y;
|
||||
const deltaY = move.baseY - currentY;
|
||||
const step = 400 * this.dt; // 换路速度
|
||||
const newY = currentY + Math.sign(deltaY) * Math.min(Math.abs(deltaY), step);
|
||||
view.node.setPosition(view.node.position.x, newY, 0);
|
||||
isChangingLane = true;
|
||||
} else {
|
||||
view.node.setPosition(view.node.position.x, move.baseY, 0);
|
||||
/** 击退位移:每帧叠加 knockbackVelocity 并指数衰减,与推进解耦 */
|
||||
if (Math.abs(move.knockbackVelocity) > 0.01) {
|
||||
const newX = view.node.position.x + move.knockbackVelocity * this.dt;
|
||||
const clampedX = this.clampByFacBoundary(newX, model.fac);
|
||||
if (Math.abs(clampedX - view.node.position.x) > 0.01) {
|
||||
view.node.setPosition(clampedX, view.node.position.y, 0);
|
||||
}
|
||||
// 时间常数 τ=0.1s:0.1 秒衰减到约 37%,0.3 秒衰减到约 5%,总位移 ≈ v0 * 0.0632
|
||||
move.knockbackVelocity *= Math.exp(-10 * this.dt);
|
||||
if (Math.abs(move.knockbackVelocity) < 0.01) {
|
||||
move.knockbackVelocity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染层级重排
|
||||
this.updateRenderOrder();
|
||||
|
||||
/** 准备阶段:双方原地待命,不推进不索敌 */
|
||||
if (!smc.mission.in_fight) {
|
||||
this.clearCombatTarget(model);
|
||||
model.is_atking = false;
|
||||
if (view.status !== "atk") view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
/** 战斗阶段:索敌并推进 */
|
||||
const nearestEnemy = this.findNearestEnemy(e);
|
||||
if (nearestEnemy) {
|
||||
/** 有敌人:进入战斗位移逻辑 */
|
||||
if (canMove) {
|
||||
this.processCombatLogic(e, move, view, model, nearestEnemy);
|
||||
} else {
|
||||
model.is_atking = true; // 战斗中不移动,但保持攻击状态
|
||||
}
|
||||
this.processCombatLogic(e, move, view, model, nearestEnemy);
|
||||
this.syncCombatTarget(model, view, nearestEnemy);
|
||||
} else {
|
||||
/** 无敌人:清目标并回归编队站位 */
|
||||
/** 无敌人:停止移动,等待战斗结束结算 */
|
||||
this.clearCombatTarget(model);
|
||||
if (canMove) {
|
||||
this.moveToSlot(view, move, model, move.targetX);
|
||||
}
|
||||
model.is_atking = false;
|
||||
if (view.status !== "atk") view.status_change("idle");
|
||||
}
|
||||
|
||||
if (canMove) {
|
||||
// 如果只在 Y 轴移动,也要播放 move 动画
|
||||
if (isChangingLane && view.status !== "move" && view.status !== "atk") {
|
||||
view.status_change("move");
|
||||
}
|
||||
} else {
|
||||
// 战斗阶段如果不移动,确保不在攻击时恢复 idle 状态
|
||||
if (!model.is_atking && view.status === "move") {
|
||||
view.status_change("idle");
|
||||
}
|
||||
}
|
||||
/** 渲染层级重排(双阵营统一处理) */
|
||||
this.updateRenderOrder();
|
||||
}
|
||||
|
||||
private clearCombatTarget(model: HeroAttrsComp): void {
|
||||
@@ -202,175 +153,67 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
return dist <= attackRange;
|
||||
}
|
||||
|
||||
private processCombatLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
const rangeType = model.type as HType.Melee | HType.Mid | HType.Long;
|
||||
switch (rangeType) {
|
||||
case HType.Melee:
|
||||
this.processMeleeLogic(e, move, view, model, enemy);
|
||||
break;
|
||||
case HType.Mid:
|
||||
this.processMidLogic(e, move, view, model, enemy);
|
||||
break;
|
||||
case HType.Long:
|
||||
this.processLongLogic(e, move, view, model, enemy);
|
||||
break;
|
||||
default:
|
||||
this.processMidLogic(e, move, view, model, enemy); // 默认中程
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 战斗位移:向最近敌人推进,进入攻击范围后停止。
|
||||
* 推进无上限限制,但击退/位移受阵营后退边界保护。
|
||||
*/
|
||||
private processCombatLogic(_e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
const selfX = view.node.position.x;
|
||||
const enemyX = enemy.node.position.x;
|
||||
const dist = Math.abs(selfX - enemyX);
|
||||
const attackRange = model.dis;
|
||||
/** 近战贴身作战:攻击距离 dis(100) 大于技能范围(60),推进到与敌人相隔 60 才停下,保证技能可命中且不过度贴身 */
|
||||
const stopRange = model.type === 0 ? 60 : attackRange;
|
||||
const inRange = dist <= stopRange;
|
||||
|
||||
private processMeleeLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
this.processFormationCombat(e, move, view, model);
|
||||
}
|
||||
|
||||
private processMidLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
this.processFormationCombat(e, move, view, model);
|
||||
}
|
||||
|
||||
private processLongLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
this.processFormationCombat(e, move, view, model);
|
||||
}
|
||||
|
||||
private processFormationCombat(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp) {
|
||||
this.moveToSlot(view, move, model, move.targetX);
|
||||
model.is_atking = true;
|
||||
}
|
||||
|
||||
private getGlobalFormationSlot(self: ecs.Entity, model: HeroAttrsComp): { targetX: number, targetY: number } {
|
||||
const allAllies: ecs.Entity[] = [];
|
||||
ecs.query(this.getHeroMoveMatcher()).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
const view = e.get(HeroViewComp);
|
||||
const move = e.get(MoveComp);
|
||||
if (!attrs || !view?.node || !move) return;
|
||||
if (attrs.is_dead || attrs.is_reviving) return;
|
||||
if (attrs.fac !== model.fac) return;
|
||||
allAllies.push(e);
|
||||
});
|
||||
|
||||
allAllies.sort((a, b) => {
|
||||
const attrsA = a.get(HeroAttrsComp);
|
||||
const attrsB = b.get(HeroAttrsComp);
|
||||
const priorityA = attrsA ? this.getCombatPriority(attrsA) : 0;
|
||||
const priorityB = attrsB ? this.getCombatPriority(attrsB) : 0;
|
||||
if (priorityA !== priorityB) return priorityB - priorityA;
|
||||
|
||||
const deadA = (attrsA?.dead && attrsA.dead.length > 0) ? 1 : 0;
|
||||
const deadB = (attrsB?.dead && attrsB.dead.length > 0) ? 1 : 0;
|
||||
if (deadA !== deadB) return deadB - deadA;
|
||||
|
||||
const lvA = attrsA?.lv ?? 1;
|
||||
const lvB = attrsB?.lv ?? 1;
|
||||
if (lvA !== lvB) return lvB - lvA;
|
||||
const moveA = a.get(MoveComp);
|
||||
const moveB = b.get(MoveComp);
|
||||
const orderA = moveA?.spawnOrder ?? 0;
|
||||
const orderB = moveB?.spawnOrder ?? 0;
|
||||
if (orderA !== orderB) return orderA - orderB;
|
||||
return a.eid - b.eid;
|
||||
});
|
||||
|
||||
const slotIndex = Math.max(0, allAllies.findIndex(entity => entity === self));
|
||||
|
||||
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
|
||||
const slotPriority = [1, 0, 2, 4, 3, 5];
|
||||
const posIndex = slotPriority[slotIndex % 6];
|
||||
const pos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
||||
|
||||
return { targetX: pos.x, targetY: pos.y };
|
||||
}
|
||||
|
||||
private moveToSlot(view: HeroViewComp, move: MoveComp, model: HeroAttrsComp, targetX: number) {
|
||||
const currentX = view.node.position.x;
|
||||
const currentY = view.node.position.y;
|
||||
|
||||
// 当 X 和 Y 都到达目标时,才算真正到达
|
||||
if (Math.abs(currentX - targetX) <= 2 && Math.abs(currentY - move.baseY) <= 2) {
|
||||
view.node.setPosition(targetX, move.baseY, 0);
|
||||
view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
const dir = targetX > currentX ? 1 : -1;
|
||||
move.direction = dir;
|
||||
const speed = model.speed / 3;
|
||||
this.moveEntity(view, dir, speed, targetX);
|
||||
}
|
||||
|
||||
private moveEntity(view: HeroViewComp, direction: number, speed: number, stopAtX?: number) {
|
||||
const model = view.ent.get(HeroAttrsComp);
|
||||
const move = view.ent.get(MoveComp);
|
||||
if (!model || !move) return;
|
||||
/** 按阵营边界裁剪,防止跑出战场可移动区域 */
|
||||
const cfg = this.facConfigs[model.fac] || this.facConfigs[FacSet.HERO];
|
||||
const moveMinX = Math.min(cfg.moveBackX, cfg.moveFrontX);
|
||||
const moveMaxX = Math.max(cfg.moveBackX, cfg.moveFrontX);
|
||||
const currentX = view.node.position.x;
|
||||
const currentY = view.node.position.y;
|
||||
|
||||
const delta = speed * this.dt * direction;
|
||||
let newX = view.node.position.x + delta;
|
||||
if (currentX < moveMinX && direction < 0) {
|
||||
// X 轴到底了,如果 Y 轴还在换路,继续维持 move 状态
|
||||
if (Math.abs(currentY - move.baseY) > 2) {
|
||||
view.status_change("move");
|
||||
} else {
|
||||
if (inRange) {
|
||||
/** 进入攻击范围:停止移动,进入攻击状态,由 SCastSystem 负责技能释放 */
|
||||
model.is_atking = true;
|
||||
const dir = enemyX > selfX ? 1 : -1;
|
||||
view.scale = dir;
|
||||
if (view.status === "move") {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (currentX > moveMaxX && direction > 0) {
|
||||
if (Math.abs(currentY - move.baseY) > 2) {
|
||||
} else {
|
||||
/** 未接触:向敌人方向推进 */
|
||||
model.is_atking = false;
|
||||
const dir = enemyX > selfX ? 1 : -1;
|
||||
move.direction = dir;
|
||||
const speed = model.speed / 3;
|
||||
const delta = speed * this.dt * dir;
|
||||
let newX = selfX + delta;
|
||||
|
||||
/** 后退边界钳制:防止被击退/位移推出屏幕 */
|
||||
newX = this.clampByFacBoundary(newX, model.fac);
|
||||
|
||||
if (Math.abs(newX - selfX) >= 0.01) {
|
||||
view.node.setPosition(newX, view.node.position.y, 0);
|
||||
view.status_change("move");
|
||||
} else {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
newX = Math.max(moveMinX, Math.min(moveMaxX, newX));
|
||||
if (stopAtX !== undefined) {
|
||||
/** 指定停止点时,限制不越过 stopAtX */
|
||||
newX = direction > 0 ? Math.min(newX, stopAtX) : Math.max(newX, stopAtX);
|
||||
}
|
||||
if (Math.abs(newX - currentX) < 0.01) {
|
||||
// X轴虽然没变化,但如果Y轴还在移动,依然是move状态
|
||||
if (Math.abs(currentY - move.baseY) > 2) {
|
||||
view.status_change("move");
|
||||
} else {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
view.node.setPosition(newX, view.node.position.y, 0); // 注意:这里只更新 X,Y 在外部平滑逻辑更新
|
||||
view.status_change("move");
|
||||
}
|
||||
|
||||
private getCombatPriority(model: HeroAttrsComp): number {
|
||||
/** 数值越大越靠前:近战 > 中程 > 远程 */
|
||||
const rangeType = model.type as HType.Melee | HType.Mid | HType.Long;
|
||||
if (rangeType === HType.Melee) return 3;
|
||||
if (rangeType === HType.Mid) return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
private resolveCombatRange(model: HeroAttrsComp, defaultMin: number, defaultMax: number): [number, number] {
|
||||
const minRange = model.getCachedMinSkillDistance();
|
||||
const maxRange = model.getCachedMaxSkillDistance();
|
||||
if (maxRange <= 0) return [defaultMin, defaultMax];
|
||||
const safeMin = Math.max(0, Math.min(minRange, maxRange - 20));
|
||||
return [safeMin, maxRange];
|
||||
/**
|
||||
* 按阵营裁剪 X 坐标,防止被击退/位移推出屏幕。
|
||||
* HERO 后退不越过 BoxSet.LETF_END;MON 后退不越过 BoxSet.RIGHT_END。
|
||||
*/
|
||||
private clampByFacBoundary(x: number, fac: number): number {
|
||||
if (fac === FacSet.HERO) {
|
||||
return Math.max(BoxSet.LETF_END, x);
|
||||
}
|
||||
return Math.min(BoxSet.RIGHT_END, x);
|
||||
}
|
||||
|
||||
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
|
||||
const currentView = entity.get(HeroViewComp);
|
||||
if (!currentView?.node) return null;
|
||||
|
||||
|
||||
const currentPos = currentView.node.position;
|
||||
const myFac = entity.get(HeroAttrsComp).fac;
|
||||
|
||||
|
||||
let nearest: HeroViewComp | null = null;
|
||||
let minDis = Infinity;
|
||||
|
||||
|
||||
/** 遍历筛出最近敌人:以 X 轴距离为主,Y 轴距离作为同排的决胜权重,使角色优先攻击同路的敌人 */
|
||||
ecs.query(this.getHeroViewMatcher()).forEach(e => {
|
||||
const m = e.get(HeroAttrsComp);
|
||||
@@ -392,38 +235,43 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
|
||||
private updateRenderOrder() {
|
||||
const scene = smc.map?.MapView?.scene;
|
||||
const actorRoot = scene?.entityLayer?.node?.getChildByName("HERO");
|
||||
if (!actorRoot) return;
|
||||
const layerNode = scene?.entityLayer?.node;
|
||||
if (!layerNode) return;
|
||||
|
||||
const now = Date.now() / 1000;
|
||||
if (now - this.lastRenderSortAt < this.renderSortInterval) return;
|
||||
this.lastRenderSortAt = now;
|
||||
|
||||
const heroRoot = layerNode.getChildByName("HERO");
|
||||
const monRoot = layerNode.getChildByName("MON");
|
||||
const bossRoot = layerNode.getChildByName("BOSS");
|
||||
|
||||
/** 分层重排同层节点:各阵营层内按 Y 轴路次 -> 前后位置 -> 出生序 -> eid 排序
|
||||
* (Boss 已有独立 BOSS 层固定在最底,无需 bossPriority 参与排序) */
|
||||
this.renderEntryCount = 0;
|
||||
ecs.query(this.getHeroViewMatcher()).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
const actorView = e.get(HeroViewComp);
|
||||
if (!attrs || !actorView?.node || attrs.is_dead) return;
|
||||
if (attrs.fac !== FacSet.HERO && attrs.fac !== FacSet.MON) return;
|
||||
|
||||
if (actorView.node.parent !== actorRoot) {
|
||||
actorView.node.parent = actorRoot;
|
||||
|
||||
/** 按阵营/身份归位到对应显示层 */
|
||||
let targetRoot: Node | null | undefined;
|
||||
if (attrs.fac === FacSet.HERO) {
|
||||
targetRoot = heroRoot;
|
||||
} else {
|
||||
targetRoot = attrs.is_boss ? (bossRoot ?? monRoot ?? heroRoot) : (monRoot ?? heroRoot);
|
||||
}
|
||||
if (!targetRoot) return;
|
||||
if (actorView.node.parent !== targetRoot) {
|
||||
actorView.node.parent = targetRoot;
|
||||
}
|
||||
|
||||
// 获取 spawnOrder,由于可能挂载 MoveComp 或 MonMoveComp,我们需要动态获取
|
||||
let spawnOrder = 0;
|
||||
const heroMove = e.get(MoveComp);
|
||||
if (heroMove) {
|
||||
spawnOrder = heroMove.spawnOrder;
|
||||
} else {
|
||||
const monMove = e.get(MonMoveComp);
|
||||
if (monMove) {
|
||||
spawnOrder = monMove.spawnOrder;
|
||||
}
|
||||
}
|
||||
/** 统一从 MoveComp 获取 spawnOrder */
|
||||
const move = e.get(MoveComp);
|
||||
const spawnOrder = move?.spawnOrder ?? 0;
|
||||
|
||||
// 按 Y 轴计算渲染层级权重(Y 越小,说明越靠近屏幕下方,应该渲染在越前面)
|
||||
// 之前的 isFly 逻辑已被移除,统一按 Y 轴处理三路渲染
|
||||
const laneScore = -actorView.node.position.y;
|
||||
// X 轴权重:站在前排的(交战处)优先渲染
|
||||
const frontScore = attrs.fac === FacSet.HERO ? actorView.node.position.x : -actorView.node.position.x;
|
||||
@@ -435,7 +283,6 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
this.renderEntries.push(entry);
|
||||
}
|
||||
entry.node = actorView.node;
|
||||
entry.bossPriority = attrs.is_boss ? 1 : 0;
|
||||
entry.laneScore = laneScore;
|
||||
entry.frontScore = frontScore;
|
||||
entry.spawnOrder = spawnOrder;
|
||||
@@ -444,9 +291,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
});
|
||||
this.renderEntries.length = this.renderEntryCount;
|
||||
|
||||
/** 定时重排同层节点:Boss 优先级 -> Y 轴路次 -> 前后位置 -> 出生序 -> eid */
|
||||
this.renderEntries.sort((a, b) => {
|
||||
if (a.bossPriority !== b.bossPriority) return a.bossPriority - b.bossPriority;
|
||||
// Y轴靠下的(laneScore 较大)应该在后面,从而渲染在上面
|
||||
if (Math.abs(a.laneScore - b.laneScore) > 10) return a.laneScore - b.laneScore;
|
||||
if (a.frontScore !== b.frontScore) return a.frontScore - b.frontScore;
|
||||
@@ -454,8 +299,14 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
return a.eid - b.eid;
|
||||
});
|
||||
|
||||
this.renderEntries.forEach((item, index) => {
|
||||
/** 分层应用 siblingIndex:各层内部从 0 递增 */
|
||||
const layerCounters = new Map<Node, number>();
|
||||
this.renderEntries.forEach((item) => {
|
||||
const parent = item.node.parent;
|
||||
if (!parent) return;
|
||||
const index = layerCounters.get(parent) ?? 0;
|
||||
item.node.setSiblingIndex(index);
|
||||
layerCounters.set(parent, index + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,6 @@ export class SkillTriggerHelper {
|
||||
case SkillTriggerType.FStart:
|
||||
this.handleArrayTrigger(model.runtime_fstart, model, view, type);
|
||||
break;
|
||||
case SkillTriggerType.FEnd:
|
||||
this.handleArrayTrigger(model.runtime_fend, model, view, type);
|
||||
break;
|
||||
case SkillTriggerType.Atking:
|
||||
this.handleAtking(model, view);
|
||||
break;
|
||||
@@ -142,7 +139,7 @@ export class SkillTriggerHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的数组型触发器处理(适用于 FStart / FEnd 等无需额外判定的简单列表触发)
|
||||
* 通用的数组型触发器处理(适用于 FStart 等无需额外判定的简单列表触发)
|
||||
*/
|
||||
private static handleArrayTrigger(configs: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[] | undefined, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
|
||||
if (!configs || configs.length === 0) return;
|
||||
|
||||
143
assets/script/game/map/BattleBannerComp.ts
Normal file
143
assets/script/game/map/BattleBannerComp.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* @file BattleBannerComp.ts
|
||||
* @description 战斗横幅统一通道组件(表现层)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 监听清屏(WaveClear)/ Boss 预警(BossWarning)/ 连杀(ComboReach)事件,播分级横幅。
|
||||
* 2. 横幅动画:右侧飞入(backOut)→ 中央停留 → 左侧飞出(backIn),与 MissionComp.playTooltipAnim 同风格。
|
||||
* 3. 清屏奖励结算(fastClear 金币 / allAlive 刷新石),走 MissionEconomy 静态接口。
|
||||
* 4. Boss 登场(BossSpawn)触发震屏 + 音效。
|
||||
*
|
||||
* 使用 oops-framework 模块:oops.message(事件解耦)、oops.audio(音效)。
|
||||
* 编辑器绑定:bannerNode(Label+背景节点,初始 active=false)。
|
||||
*/
|
||||
import { _decorator, Node, Label, tween, Tween, v3, Color } from "cc";
|
||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { MissionEconomy } from "./MissionEconomy";
|
||||
import { ScreenShake } from "../common/ScreenShake";
|
||||
import { WAVE_DURATION } from "./RogueConfig";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 清屏奖励:迅速清场(< 75% 时长) */
|
||||
const FAST_CLEAR_COIN = 2;
|
||||
/** 清屏奖励:极速清场(< 50% 时长) */
|
||||
const ULTRA_CLEAR_COIN = 5;
|
||||
/** 连杀分级金币爆发(tier 0/1/2 对应 5/10/20 连杀) */
|
||||
const COMBO_COIN = [2, 5, 10];
|
||||
|
||||
@ccclass('BattleBannerComp')
|
||||
export class BattleBannerComp extends CCComp {
|
||||
/** 横幅节点(Label+背景,初始隐藏),编辑器拖拽绑定 */
|
||||
@property({ type: Node, tooltip: "横幅节点(Label+背景,初始 active=false)" })
|
||||
bannerNode: Node | null = null;
|
||||
|
||||
/** 上次横幅播放时间戳(去抖:0.3s 内的新横幅直接打断旧的,不叠加 tween) */
|
||||
private lastBannerTime: number = 0;
|
||||
|
||||
onLoad() {
|
||||
// oops.message 全局事件总线:清屏 / Boss 预警 / Boss 登场 / 连杀
|
||||
oops.message.on(GameEvent.WaveClear, this.onWaveClear, this);
|
||||
oops.message.on(GameEvent.BossWarning, this.onBossWarning, this);
|
||||
oops.message.on(GameEvent.BossSpawn, this.onBossSpawn, this);
|
||||
oops.message.on(GameEvent.ComboReach, this.onComboReach, this);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
oops.message.off(GameEvent.WaveClear, this.onWaveClear, this);
|
||||
oops.message.off(GameEvent.BossWarning, this.onBossWarning, this);
|
||||
oops.message.off(GameEvent.BossSpawn, this.onBossSpawn, this);
|
||||
oops.message.off(GameEvent.ComboReach, this.onComboReach, this);
|
||||
}
|
||||
|
||||
// ======================== 事件处理 ========================
|
||||
|
||||
/** 清屏庆祝:分级文案 + 奖励结算 */
|
||||
private onWaveClear(event: string, data: { wave: number; clearTime: number; allAlive: boolean; fastClear: number }) {
|
||||
if (data.allAlive) {
|
||||
this.playBanner("Perfect!", new Color(255, 80, 80), 1.2);
|
||||
MissionEconomy.addRefreshStone(1);
|
||||
oops.audio.playEffect("music/flash");
|
||||
} else if (data.fastClear === 2) {
|
||||
this.playBanner("极速清场!", new Color(255, 170, 0), 1.15);
|
||||
MissionEconomy.addCoin(ULTRA_CLEAR_COIN);
|
||||
oops.audio.playEffect("music/flash");
|
||||
} else if (data.fastClear === 1) {
|
||||
this.playBanner("迅速清场!", new Color(255, 220, 60), 1.05);
|
||||
MissionEconomy.addCoin(FAST_CLEAR_COIN);
|
||||
} else {
|
||||
this.playBanner("Clear!", new Color(255, 255, 255), 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
/** Boss 回合预警:红字横幅 */
|
||||
private onBossWarning(event: string, data: { wave: number; eta: number }) {
|
||||
this.playBanner("强敌来袭!", new Color(255, 60, 60), 1.3);
|
||||
oops.audio.playEffect("music/flash");
|
||||
}
|
||||
|
||||
/** Boss 登场:震屏 + 闷响 */
|
||||
private onBossSpawn() {
|
||||
ScreenShake.shake(10, 0.4);
|
||||
oops.audio.playEffect("music/dun");
|
||||
}
|
||||
|
||||
/** 连杀达阈值:分级文案 + 震屏 + 金币爆发 */
|
||||
private onComboReach(event: string, data: { count: number; tier: number }) {
|
||||
const tier = Math.min(data.tier, 2);
|
||||
const colors = [new Color(255, 230, 80), new Color(255, 150, 30), new Color(255, 60, 60)];
|
||||
const scales = [1.0, 1.2, 1.4];
|
||||
this.playBanner(`${data.count} 连杀!`, colors[tier], scales[tier]);
|
||||
if (tier === 1) ScreenShake.shake(4, 0.2);
|
||||
if (tier === 2) ScreenShake.shake(8, 0.3);
|
||||
MissionEconomy.addCoin(COMBO_COIN[tier]);
|
||||
// 占位音效分级(后续可替换为升调 combo 音效)
|
||||
oops.audio.playEffect(tier === 2 ? "music/Fire" : tier === 1 ? "music/Critical" : "music/Hit");
|
||||
}
|
||||
|
||||
// ======================== 横幅播放 ========================
|
||||
|
||||
/**
|
||||
* 播放横幅:右进 → 中央停留 → 左出
|
||||
* @param text 文案
|
||||
* @param color 文字颜色
|
||||
* @param scale 整体缩放(分级表现力)
|
||||
*/
|
||||
private playBanner(text: string, color: Color, scale: number = 1.0) {
|
||||
if (!this.bannerNode || !this.bannerNode.isValid) return;
|
||||
|
||||
// 去抖:0.3s 内的新横幅直接打断旧的
|
||||
const now = Date.now();
|
||||
if (now - this.lastBannerTime < 300) {
|
||||
Tween.stopAllByTarget(this.bannerNode);
|
||||
}
|
||||
this.lastBannerTime = now;
|
||||
|
||||
this.bannerNode.active = true;
|
||||
this.bannerNode.setScale(v3(scale, scale, 1));
|
||||
|
||||
const label = this.bannerNode.getComponentInChildren(Label);
|
||||
if (label) {
|
||||
label.string = text;
|
||||
label.color = color;
|
||||
label.updateRenderData(true);
|
||||
}
|
||||
|
||||
Tween.stopAllByTarget(this.bannerNode);
|
||||
const startPos = v3(1200, 0, 0);
|
||||
const centerPos = v3(0, 0, 0);
|
||||
const endPos = v3(-1200, 0, 0);
|
||||
this.bannerNode.setPosition(startPos);
|
||||
|
||||
tween(this.bannerNode)
|
||||
.to(0.4, { position: centerPos }, { easing: "backOut" })
|
||||
.to(0.8, { position: v3(-40, 0, 0) }, { easing: "sineInOut" })
|
||||
.to(0.35, { position: endPos }, { easing: "backIn" })
|
||||
.call(() => {
|
||||
this.bannerNode!.active = false;
|
||||
})
|
||||
.start();
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/BattleBannerComp.ts.meta
Normal file
9
assets/script/game/map/BattleBannerComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "676e7123-f3e8-4e9e-8942-b1c025c6708e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -285,11 +285,11 @@ export class CHeroComp extends CCComp {
|
||||
// 背景:按 card_lv 激活对应颜色子节点(1→green 2→blue 3→purple 4→red 5→yellow)
|
||||
this.updateBgNode();
|
||||
|
||||
// 描述富文本:不再单独手写 info,统一套用英雄 1 级能力介绍(buildSkillDescMoreRich)
|
||||
// 描述富文本:统一套用英雄 5 级能力介绍(buildSkillDescMoreRich)
|
||||
// 与英雄信息弹窗/升级确认框同一套文案规则,杜绝手写文案与数值漂移
|
||||
// outlineRich 为整段富文本包裹描边,提升深色面板可读性
|
||||
if (this.info_rich) {
|
||||
const desc = hero ? buildSkillDescMoreRich(this.buildHeroDescSource(hero)) : (this.cardData.info || "");
|
||||
const desc = hero ? buildSkillDescMoreRich(this.buildHeroDescSource(hero), false) : (this.cardData.info || "");
|
||||
this.info_rich.string = outlineRich(desc);
|
||||
this.info_rich.node.active = !!desc;
|
||||
}
|
||||
@@ -467,18 +467,17 @@ export class CHeroComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造英雄 1 级能力描述数据源(ISkillDescSource)。
|
||||
* lv 恒为 1,由各 resolver 取 1 级生效档,与英雄信息弹窗初始展示口径一致。
|
||||
* 构造英雄 5 级能力描述数据源(ISkillDescSource)。
|
||||
* lv 恒为 5,由各 resolver 取 5 级生效档,展示英雄满级能力信息。
|
||||
* @param hero HeroInfo 静态配置项
|
||||
* @returns 描述数据源
|
||||
*/
|
||||
private buildHeroDescSource(hero: typeof HeroInfo[number]): ISkillDescSource {
|
||||
return {
|
||||
lv: 1,
|
||||
lv: 5,
|
||||
call: hero.call,
|
||||
dead: hero.dead,
|
||||
fstart: hero.fstart,
|
||||
fend: hero.fend,
|
||||
atking: hero.atking,
|
||||
atked: hero.atked,
|
||||
field: hero.field,
|
||||
|
||||
101
assets/script/game/map/CoinFlyComp.ts
Normal file
101
assets/script/game/map/CoinFlyComp.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @file CoinFlyComp.ts
|
||||
* @description 金币飞行表现组件(表现层,纯视觉)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 监听 CoinFly 事件(怪物死亡掉金币,账务已由 MissionEconomy 即时结算)。
|
||||
* 2. 从怪物位置生成金币 icon,抛物线散开后加速飞入钱包 icon。
|
||||
* 3. 独立 NodePool 管理金币节点(上限 30),不复用伤害飘字池。
|
||||
*
|
||||
* 使用 oops-framework 模块:oops.message(事件解耦)。
|
||||
* 编辑器绑定:flyLayer(全屏容器,最高 sibling)、coinIconSrc(钱包 coin/icon 节点,取其 spriteFrame)。
|
||||
*/
|
||||
import { _decorator, Node, Sprite, UITransform, tween, v3, Vec3, NodePool } from "cc";
|
||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 普通怪金币枚数 */
|
||||
const COIN_COUNT_NORMAL = 3;
|
||||
/** Boss 金币枚数 */
|
||||
const COIN_COUNT_BOSS = 8;
|
||||
/** 对象池上限(防节点泄漏) */
|
||||
const POOL_MAX = 30;
|
||||
|
||||
@ccclass('CoinFlyComp')
|
||||
export class CoinFlyComp extends CCComp {
|
||||
@property({ type: Node, tooltip: "金币飞行容器(全屏节点,置于最高 sibling 盖在所有 UI 之上)" })
|
||||
flyLayer: Node | null = null;
|
||||
|
||||
@property({ type: Node, tooltip: "钱包金币 icon 节点(运行期取其 spriteFrame 克隆)" })
|
||||
coinIconSrc: Node | null = null;
|
||||
|
||||
/** 金币节点对象池 */
|
||||
private pool: NodePool = new NodePool();
|
||||
|
||||
onLoad() {
|
||||
// oops.message 全局事件总线:金币飞行
|
||||
oops.message.on(GameEvent.CoinFly, this.onCoinFly, this);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
oops.message.off(GameEvent.CoinFly, this.onCoinFly, this);
|
||||
this.pool.clear();
|
||||
}
|
||||
|
||||
private onCoinFly(event: string, data: { worldPos: Vec3 | null; gold: number; isBoss: boolean }) {
|
||||
if (!this.flyLayer || !this.coinIconSrc || !data.worldPos) return;
|
||||
|
||||
const uiTransform = this.flyLayer.getComponent(UITransform);
|
||||
if (!uiTransform) return;
|
||||
|
||||
const startPos = uiTransform.convertToNodeSpaceAR(data.worldPos);
|
||||
const endPos = uiTransform.convertToNodeSpaceAR(this.coinIconSrc.worldPosition);
|
||||
const count = data.isBoss ? COIN_COUNT_BOSS : COIN_COUNT_NORMAL;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.spawnCoin(startPos, endPos, i * 0.03);
|
||||
}
|
||||
}
|
||||
|
||||
/** 生成一枚金币:上抛散开 → 加速飞向钱包 → 回收 */
|
||||
private spawnCoin(startPos: Vec3, endPos: Vec3, delay: number) {
|
||||
const coin = this.pool.size() > 0 ? this.pool.get()! : this.createCoinNode();
|
||||
coin.parent = this.flyLayer;
|
||||
coin.setPosition(startPos);
|
||||
coin.setScale(v3(1, 1, 1));
|
||||
|
||||
const scatter = v3(
|
||||
startPos.x + (Math.random() * 120 - 60),
|
||||
startPos.y + 60 + Math.random() * 40,
|
||||
0);
|
||||
|
||||
tween(coin)
|
||||
.delay(delay)
|
||||
.to(0.25, { position: scatter }, { easing: "quadOut" })
|
||||
.to(0.4, { position: endPos }, { easing: "quadIn" })
|
||||
.call(() => this.recycleCoin(coin))
|
||||
.start();
|
||||
}
|
||||
|
||||
private createCoinNode(): Node {
|
||||
const node = new Node("coin_fly");
|
||||
node.addComponent(UITransform).setContentSize(32, 32);
|
||||
const sp = node.addComponent(Sprite);
|
||||
const srcSp = this.coinIconSrc?.getComponent(Sprite);
|
||||
if (srcSp?.spriteFrame) {
|
||||
sp.spriteFrame = srcSp.spriteFrame;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private recycleCoin(coin: Node) {
|
||||
if (this.pool.size() >= POOL_MAX) {
|
||||
coin.destroy();
|
||||
return;
|
||||
}
|
||||
this.pool.put(coin);
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/CoinFlyComp.ts.meta
Normal file
9
assets/script/game/map/CoinFlyComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "f607a68a-c2e6-4ebb-8439-569a24143e6e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
72
assets/script/game/map/ComboComp.ts
Normal file
72
assets/script/game/map/ComboComp.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @file ComboComp.ts
|
||||
* @description 连杀 Combo 计数组件(逻辑层,事件驱动)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 监听 MonDead 事件,2s 滑动窗口内累计连杀数。
|
||||
* 2. 达阈值(5/10/20)时派发 ComboReach 事件,由 BattleBannerComp 消费分级表现。
|
||||
* 3. 窗口超时或整局结束(MissionEnd)清零。
|
||||
*
|
||||
* 使用 oops-framework 模块:oops.message(事件解耦)。
|
||||
* 本组件无 UI 绑定,挂 mission.prefab 任意节点即可。
|
||||
*/
|
||||
import { _decorator } from "cc";
|
||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
/** 连杀窗口(秒):窗口内每次击杀续期 */
|
||||
const COMBO_WINDOW = 2;
|
||||
/** 连杀阈值档位:x5 / x10 / x20 */
|
||||
const COMBO_TIERS = [5, 10, 20];
|
||||
|
||||
@ccclass('ComboComp')
|
||||
export class ComboComp extends CCComp {
|
||||
/** 当前连杀数 */
|
||||
private comboCount: number = 0;
|
||||
/** 滑动窗口剩余时间(秒) */
|
||||
private windowTimer: number = 0;
|
||||
|
||||
onLoad() {
|
||||
// oops.message 全局事件总线:怪物死亡 / 整局结束
|
||||
oops.message.on(GameEvent.MonDead, this.onMonDead, this);
|
||||
oops.message.on(GameEvent.MissionEnd, this.resetCombo, this);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
oops.message.off(GameEvent.MonDead, this.onMonDead, this);
|
||||
oops.message.off(GameEvent.MissionEnd, this.resetCombo, this);
|
||||
}
|
||||
|
||||
private onMonDead() {
|
||||
this.comboCount++;
|
||||
this.windowTimer = COMBO_WINDOW;
|
||||
|
||||
const tier = COMBO_TIERS.indexOf(this.comboCount);
|
||||
if (tier >= 0) {
|
||||
// oops.message 全局事件总线:连杀达阈值(横幅/震屏/金币爆发由 BattleBannerComp 消费)
|
||||
oops.message.dispatchEvent(GameEvent.ComboReach, {
|
||||
count: this.comboCount,
|
||||
tier,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private resetCombo() {
|
||||
this.comboCount = 0;
|
||||
this.windowTimer = 0;
|
||||
}
|
||||
|
||||
protected update(dt: number) {
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
if (this.windowTimer > 0) {
|
||||
this.windowTimer -= dt;
|
||||
if (this.windowTimer <= 0) {
|
||||
this.comboCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/ComboComp.ts.meta
Normal file
9
assets/script/game/map/ComboComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1bf46bb7-2f09-4487-9551-3672912c0afb",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每回合次数)
|
||||
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
|
||||
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
|
||||
* - FightEnd (5):监听 FightEnd 事件(每回合结束派发),按 trigger_limit 全局累计上限
|
||||
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
|
||||
* - HeroCall (7):监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
@@ -37,6 +36,7 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
|
||||
import { CardTriggerType } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { SkillSet, SkillOverrides, FieldSkillSet } from "../common/config/SkillSet";
|
||||
import { FieldEntry } from "../common/config/heroSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
@@ -93,8 +93,8 @@ export class EquipBoxComp extends CCComp {
|
||||
private keep_waves: number = 0;
|
||||
/** 技能覆写参数(自定义伤害、Buff等) */
|
||||
private overrides?: SkillOverrides;
|
||||
/** 驻场技能 UUID 列表 */
|
||||
public field: number[] = [];
|
||||
/** 驻场技能词条列表(uuid 指向 FieldSkillSet 底座,value 为生效数值) */
|
||||
public field: FieldEntry[] = [];
|
||||
/** 卡牌自定义图标ID(优先级最高,未定义则按 trigger_type 自动取) */
|
||||
private card_icon?: string;
|
||||
|
||||
@@ -144,7 +144,6 @@ export class EquipBoxComp extends CCComp {
|
||||
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onFightStart, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.MasterCalled, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.ReviveSuccess, this.onEventTrigger, this);
|
||||
@@ -286,7 +285,6 @@ export class EquipBoxComp extends CCComp {
|
||||
* - Interval: 监听 FightStart,进入战斗后由 update 帧驱动计时
|
||||
* - Field: 不主动施法(实际生效由 FieldSkillSet 处理)
|
||||
* - FightStart: 监听 FightStart 事件
|
||||
* - FightEnd: 监听 FightEnd 事件
|
||||
* - HeroDead: 监听 HeroDead 事件(已在派发处做阵营过滤)
|
||||
* - HeroCall: 监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
@@ -312,10 +310,6 @@ export class EquipBoxComp extends CCComp {
|
||||
oops.message.on(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.FightEnd:
|
||||
oops.message.on(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.HeroDead:
|
||||
oops.message.on(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
break;
|
||||
@@ -337,7 +331,7 @@ export class EquipBoxComp extends CCComp {
|
||||
* 事件型触发的统一入口:
|
||||
* - Instant 类型:按 trigger_times 上限判定,复用 current_trigger_times 跟踪
|
||||
* (保持与原 is_instant 行为一致,且 NewWave 中也用 current_trigger_times)
|
||||
* - 事件型(FightStart/FightEnd/HeroDead/HeroCall):按 trigger_limit 上限判定,使用 trigger_count 跟踪
|
||||
* - 事件型(FightStart/HeroDead/HeroCall):按 trigger_limit 上限判定,使用 trigger_count 跟踪
|
||||
*
|
||||
* 注意:本方法不读取事件 payload,仅作触发信号使用(避免 MasterCalled 不同 payload 字段引发的兼容问题)。
|
||||
*/
|
||||
@@ -425,7 +419,7 @@ export class EquipBoxComp extends CCComp {
|
||||
if (this.trigger_type === CardTriggerType.Interval) {
|
||||
iconId = this.s_uuid ? SkillSet[this.s_uuid]?.icon : undefined;
|
||||
} else if (this.trigger_type === CardTriggerType.Field) {
|
||||
const fieldUuid = this.field?.[0];
|
||||
const fieldUuid = this.field?.[0]?.uuid;
|
||||
iconId = fieldUuid ? FieldSkillSet[fieldUuid]?.icon : undefined;
|
||||
}
|
||||
}
|
||||
@@ -444,7 +438,6 @@ export class EquipBoxComp extends CCComp {
|
||||
const showRemainCount =
|
||||
this.trigger_type === CardTriggerType.Interval ||
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
|
||||
@@ -453,7 +446,6 @@ export class EquipBoxComp extends CCComp {
|
||||
// 事件型按 trigger_limit;Interval 按 t_times
|
||||
const isEvent =
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
const used = isEvent ? this.trigger_count : this.current_trigger_times;
|
||||
@@ -513,7 +505,7 @@ export class EquipBoxComp extends CCComp {
|
||||
* 处理维持回合逻辑:递减剩余回合,或者重置触发次数。
|
||||
*
|
||||
* 各类型在新一回合的行为:
|
||||
* - Instant/Interval/FightStart/FightEnd:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
|
||||
* - Instant/Interval/FightStart:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
|
||||
* - Field:被动生效,跟随 keep_waves 决定存活
|
||||
* - HeroDead/HeroCall:跨回合触发的事件型,trigger_count(全局)不重置,仅 keep_waves 控制存活
|
||||
*/
|
||||
|
||||
@@ -584,7 +584,7 @@ export class HInfoComp extends CCComp {
|
||||
if (config.trigger_type === CardTriggerType.Interval) {
|
||||
iconId = config.skill ? SkillSet[config.skill]?.icon : undefined;
|
||||
} else if (config.trigger_type === CardTriggerType.Field) {
|
||||
const fieldUuid = config.field?.[0];
|
||||
const fieldUuid = config.field?.[0]?.uuid;
|
||||
iconId = fieldUuid ? FieldSkillSet[fieldUuid]?.icon : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user