feat(monster&spawn): 新增飞行怪物支持,重构怪物移动与刷怪系统
抽离MonMoveComp拆分怪物移动逻辑,让MoveComp仅负责英雄移动 新增Fly和FlyBoss怪物类型,配置三层飞行轨道支持空中怪物 重写波次刷怪逻辑,移除固定5槽限制,按轨道自由排布怪物 将怪物生成上限与恢复阈值从5/3调整为50/30 优化渲染排序逻辑,为飞行怪添加持续浮动动画 移除跨波怪物属性继承,波次切换时自动清理残留怪物
This commit is contained in:
@@ -3,18 +3,15 @@
|
||||
* @description 怪物(Monster)波次刷新管理组件(逻辑层)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 管理每一波怪物的 **生成计划**:根据 WaveSlotConfig 分配怪物到固定槽位。
|
||||
* 2. 管理 5 个固定刷怪槽位的占用状态,支持 Boss 占 2 格。
|
||||
* 3. 处理特殊插队刷怪请求(MonQueue),优先于常规刷新。
|
||||
* 4. 自动推进波次:当前波所有怪物被清除后自动进入下一波。
|
||||
* 1. 管理每一波怪物的 **生成计划**:根据 WaveSlotConfig 生成怪物。
|
||||
* 2. 处理特殊插队刷怪请求(MonQueue),优先于常规刷新。
|
||||
* 3. 自动推进波次:当前波所有怪物被清除后自动进入下一波。
|
||||
*
|
||||
* 关键设计:
|
||||
* - 全场固定 5 个槽位(索引 0-4),每个槽位占固定 X 坐标。
|
||||
* - Boss 默认占 3 个连续槽位,只要有连续三格空闲即可。
|
||||
* - slotOccupiedEids 记录每个槽位占用的怪物 ECS 实体 ID。
|
||||
* - 突破 5 槽限制,怪物按刷怪顺序依次从 X=280 开始向右(每隔 50)排布。
|
||||
* - 3 条刷怪线:地面、+120、+240。
|
||||
* - resetSlotSpawnData(wave) 在每波开始时读取配置,分配并立即生成所有怪物。
|
||||
* - refreshSlotOccupancy() 定期检查槽位占用的实体是否仍存活,清除已死亡的占用。
|
||||
* - tryAdvanceWave() 在所有怪物死亡后自动推进波次。
|
||||
* - 去除跨波 HP 继承,上一波残留怪在波次结束/开始时销毁。
|
||||
*
|
||||
* 怪物属性计算公式:
|
||||
* ap = floor((base_ap + stage × grow_ap) × SpawnPowerBias)
|
||||
@@ -25,7 +22,7 @@
|
||||
* - RogueConfig —— 怪物类型、成长值、波次配置
|
||||
* - Monster(hero/Mon.ts)—— 怪物 ECS 实体类
|
||||
* - HeroInfo(heroSet)—— 怪物基础属性配置(与英雄共用配置)
|
||||
* - HeroAttrsComp / MoveComp —— 怪物属性和移动组件
|
||||
* - HeroAttrsComp / MonMoveComp —— 怪物属性和移动组件
|
||||
* - BoxSet.GAME_LINE —— 地面基准 Y 坐标
|
||||
*/
|
||||
import { _decorator, v3, Vec3 } from "cc";
|
||||
@@ -37,33 +34,31 @@ import { Monster } from "../hero/Mon";
|
||||
import { HeroInfo, HType } from "../common/config/heroSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import {BoxSet } from "../common/config/GameSet";
|
||||
import {BoxSet, FacSet } from "../common/config/GameSet";
|
||||
import { MonList, MonType, SpawnPowerBias, StageBossGrow, StageGrow, UpType, WaveSlotConfig, DefaultWaveSlot, IWaveSlot } from "./RogueConfig";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { MoveComp } from "../hero/MoveComp";
|
||||
import { MonMoveComp } from "../hero/MonMoveComp";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* MissionMonCompComp —— 怪物波次刷新管理器
|
||||
*
|
||||
* 每波开始时根据 WaveSlotConfig 配置分配怪物到固定槽位,
|
||||
* 战斗中监控槽位状态,所有怪物消灭后自动推进到下一波。
|
||||
* 每波开始时根据 WaveSlotConfig 配置生成怪物,
|
||||
* 战斗中监控数量,所有怪物消灭后自动推进到下一波。
|
||||
*/
|
||||
@ccclass('MissionMonCompComp')
|
||||
@ecs.register('MissionMonComp', false)
|
||||
export class MissionMonCompComp extends CCComp {
|
||||
// ======================== 常量 ========================
|
||||
|
||||
/** Boss 的渲染优先级偏移(确保 Boss 始终渲染在最前) */
|
||||
private static readonly BOSS_RENDER_PRIORITY = 1000000;
|
||||
/** 第一个槽位的 X 坐标起点 */
|
||||
private static readonly MON_SLOT_START_X = 50;
|
||||
/** 槽位间的 X 间距 */
|
||||
private static readonly MON_SLOT_X_INTERVAL = 65;
|
||||
/** 怪物出生点起点 X */
|
||||
private static readonly MON_SPAWN_START_X = 280;
|
||||
/** 怪物出生的 X 间距 */
|
||||
private static readonly MON_SPAWN_GAP_X = 50;
|
||||
/** 怪物出生掉落高度 */
|
||||
private static readonly MON_DROP_HEIGHT = 280;
|
||||
/** 最大槽位数 */
|
||||
private static readonly MAX_SLOTS = 5;
|
||||
/** 飞行层高度偏移(地面, 空中1, 空中2) */
|
||||
private static readonly FLY_LANE_Y_OFFSETS = [0, 120, 240];
|
||||
|
||||
// ======================== 编辑器属性 ========================
|
||||
|
||||
@@ -81,12 +76,14 @@ export class MissionMonCompComp extends CCComp {
|
||||
uuid: number,
|
||||
/** 怪物等级 */
|
||||
level: number,
|
||||
/** 飞行层 */
|
||||
flyLane: number,
|
||||
}> = [];
|
||||
|
||||
// ======================== 运行时状态 ========================
|
||||
|
||||
/** 槽位占用状态:记录每个槽位当前占用的怪物 ECS 实体 ID,null 表示空闲 */
|
||||
private slotOccupiedEids: Array<number | null> = Array(5).fill(null);
|
||||
/** 记录每条线当前排到的索引 */
|
||||
private laneIndices: number[] = [0, 0, 0];
|
||||
/** 全局生成顺序计数器(用于渲染层级排序) */
|
||||
private globalSpawnOrder: number = 0;
|
||||
/** 插队刷怪处理计时器 */
|
||||
@@ -110,17 +107,13 @@ export class MissionMonCompComp extends CCComp {
|
||||
/**
|
||||
* 帧更新:
|
||||
* 1. 检查游戏是否运行中。
|
||||
* 2. 刷新槽位占用状态(清除已死亡怪物的占用)。
|
||||
* 3. 尝试推进波次(所有怪物清除后自动进入下一波)。
|
||||
* 4. 处理插队刷怪队列。
|
||||
* 2. 处理插队刷怪队列。
|
||||
*/
|
||||
protected update(dt: number): void {
|
||||
if(!smc.mission.play) return
|
||||
if(smc.mission.pause) return
|
||||
if(smc.mission.stop_mon_action) return;
|
||||
if(!smc.mission.in_fight) return;
|
||||
this.refreshSlotOccupancy();
|
||||
if(!smc.mission.in_fight) return;
|
||||
if(smc.mission.stop_spawn_mon) return;
|
||||
this.updateSpecialQueue(dt);
|
||||
}
|
||||
@@ -130,7 +123,7 @@ export class MissionMonCompComp extends CCComp {
|
||||
/**
|
||||
* 接收特殊刷怪事件并入队。
|
||||
* @param event 事件名
|
||||
* @param args { uuid: number, level: number }
|
||||
* @param args { uuid: number, level: number, flyLane?: number }
|
||||
*/
|
||||
private onSpawnSpecialMonster(event: string, args: any) {
|
||||
if (!args) return;
|
||||
@@ -138,6 +131,7 @@ export class MissionMonCompComp extends CCComp {
|
||||
this.MonQueue.push({
|
||||
uuid: args.uuid,
|
||||
level: args.level,
|
||||
flyLane: args.flyLane || 0
|
||||
});
|
||||
// 加速队列消费
|
||||
this.queueTimer = 1.0;
|
||||
@@ -158,11 +152,12 @@ export class MissionMonCompComp extends CCComp {
|
||||
this.waveTargetCount = 0
|
||||
this.waveSpawnedCount = 0
|
||||
this.MonQueue = []
|
||||
this.laneIndices = [0, 0, 0];
|
||||
|
||||
let hasBoss = false;
|
||||
const config = WaveSlotConfig[this.currentWave] || DefaultWaveSlot;
|
||||
for (const slot of config) {
|
||||
if (slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss) {
|
||||
if (slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss || slot.type === MonType.FlyBoss) {
|
||||
hasBoss = true;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +167,6 @@ export class MissionMonCompComp extends CCComp {
|
||||
bossWave: hasBoss,
|
||||
});
|
||||
|
||||
// 不再直接调用 startNextWave(),等待进入 PrepareEnd 阶段再刷怪
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
|
||||
}
|
||||
|
||||
@@ -180,54 +174,26 @@ export class MissionMonCompComp extends CCComp {
|
||||
|
||||
/**
|
||||
* 处理插队刷怪队列(每 0.15 秒尝试消费一个):
|
||||
* 1. 判断怪物是否为 Boss(决定占用 1 格还是 2 格)。
|
||||
* 2. 在空闲槽位中查找合适位置。
|
||||
* 3. 找到后从队列中移除并生成怪物。
|
||||
* 1. 根据飞行层排号。
|
||||
* 2. 找到后从队列中移除并生成怪物。
|
||||
*/
|
||||
private updateSpecialQueue(dt: number) {
|
||||
if (this.MonQueue.length <= 0) return;
|
||||
this.queueTimer += dt;
|
||||
if (this.queueTimer < 0.15) return;
|
||||
|
||||
const item = this.MonQueue[0];
|
||||
const isBoss = MonList[MonType.MeleeBoss].includes(item.uuid) || MonList[MonType.LongBoss].includes(item.uuid);
|
||||
const isLongBoss = MonList[MonType.LongBoss].includes(item.uuid);
|
||||
const slotsPerMon = isBoss ? 3 : 1;
|
||||
const item = this.MonQueue.shift()!;
|
||||
this.queueTimer = 0;
|
||||
|
||||
// 查找空闲槽位
|
||||
let slotIndex = -1;
|
||||
if (slotsPerMon === 3) {
|
||||
// 构造可用索引列表
|
||||
let allowedIndices = [];
|
||||
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS - 2; i++) {
|
||||
allowedIndices.push(i);
|
||||
}
|
||||
// 远程 Boss 插队时优先尝试从后往前找
|
||||
if (isLongBoss) {
|
||||
allowedIndices.reverse();
|
||||
}
|
||||
for (const idx of allowedIndices) {
|
||||
if (!this.slotOccupiedEids[idx] && !this.slotOccupiedEids[idx + 1] && !this.slotOccupiedEids[idx + 2]) {
|
||||
slotIndex = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 普通怪找第一个空闲格
|
||||
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) {
|
||||
if (!this.slotOccupiedEids[i]) {
|
||||
slotIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const isBoss = MonList[MonType.MeleeBoss].includes(item.uuid) ||
|
||||
MonList[MonType.LongBoss].includes(item.uuid) ||
|
||||
(MonList[MonType.FlyBoss] && MonList[MonType.FlyBoss].includes(item.uuid));
|
||||
|
||||
if (slotIndex !== -1) {
|
||||
this.MonQueue.shift();
|
||||
this.queueTimer = 0;
|
||||
const upType = this.getRandomUpType();
|
||||
this.addMonsterBySlot(slotIndex, item.uuid, isBoss, upType, Math.max(1, Number(item.level ?? 1)), slotsPerMon);
|
||||
}
|
||||
const upType = this.getRandomUpType();
|
||||
const lane = item.flyLane >= 0 && item.flyLane <= 2 ? item.flyLane : 0;
|
||||
|
||||
this.addMonsterAt(lane, this.laneIndices[lane], item.uuid, isBoss, upType, Math.max(1, Number(item.level ?? 1)));
|
||||
this.laneIndices[lane]++;
|
||||
}
|
||||
|
||||
// ======================== 波次管理 ========================
|
||||
@@ -246,7 +212,7 @@ export class MissionMonCompComp extends CCComp {
|
||||
let hasBoss = false;
|
||||
const config = WaveSlotConfig[this.currentWave] || DefaultWaveSlot;
|
||||
for (const slot of config) {
|
||||
if (slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss) {
|
||||
if (slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss || slot.type === MonType.FlyBoss) {
|
||||
hasBoss = true;
|
||||
}
|
||||
}
|
||||
@@ -311,30 +277,45 @@ export class MissionMonCompComp extends CCComp {
|
||||
// ======================== 槽位管理 ========================
|
||||
|
||||
/**
|
||||
* 重置槽位并生成本波所有怪物:
|
||||
* 1. 读取波次配置(WaveSlotConfig 或 DefaultWaveSlot)。
|
||||
* 2. 将 Boss 和普通怪分类。
|
||||
* 3. Boss 优先分配到 0, 2, 4 号位(占 2 格)。
|
||||
* 4. 普通怪填充剩余空闲格。
|
||||
* 5. 立即实例化所有怪物。
|
||||
* 重新分配并生成本波所有怪物:
|
||||
* 1. 清理上一波残留怪物。
|
||||
* 2. 读取波次配置。
|
||||
* 3. 依据配置和 flyLane 属性,为每只怪物分配自增索引。
|
||||
* 4. 立即实例化所有怪物。
|
||||
*
|
||||
* @param wave 当前波数
|
||||
*/
|
||||
private resetSlotSpawnData(wave: number = 1) {
|
||||
// 1. 清理上一波残留怪物
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
if (attrs && attrs.fac === FacSet.MON && !attrs.is_dead) {
|
||||
e.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 读取波次配置
|
||||
const config: IWaveSlot[] = WaveSlotConfig[wave] || DefaultWaveSlot;
|
||||
const oldOccupiedEids = [...this.slotOccupiedEids];
|
||||
this.slotOccupiedEids = Array(MissionMonCompComp.MAX_SLOTS).fill(null);
|
||||
|
||||
// 3. 重置排号索引
|
||||
this.laneIndices = [0, 0, 0];
|
||||
|
||||
let allMons: any[] = [];
|
||||
|
||||
// 解析配置
|
||||
for (const slot of config) {
|
||||
const isBoss = slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss;
|
||||
const slotsPerMon = slot.slotsPerMon || (isBoss ? 3 : 1);
|
||||
const isBoss = slot.type === MonType.MeleeBoss || slot.type === MonType.LongBoss || slot.type === MonType.FlyBoss;
|
||||
// 判断分配到的飞行层
|
||||
let lane: number = slot.flyLane !== undefined ? slot.flyLane : 0;
|
||||
if (slot.type === MonType.Fly || slot.type === MonType.FlyBoss) {
|
||||
lane = slot.flyLane !== undefined ? slot.flyLane : 1; // 飞行怪默认在第一层
|
||||
}
|
||||
lane = Math.max(0, Math.min(2, lane)); // 约束在 0,1,2
|
||||
|
||||
for (let i = 0; i < slot.count; i++) {
|
||||
const uuid = this.getRandomUuidByType(slot.type);
|
||||
const upType = this.getRandomUpType();
|
||||
const req = { uuid, isBoss, upType, monLv: wave, slotsPerMon };
|
||||
const req = { uuid, isBoss, upType, monLv: wave, lane };
|
||||
allMons.push(req);
|
||||
}
|
||||
}
|
||||
@@ -342,166 +323,48 @@ export class MissionMonCompComp extends CCComp {
|
||||
this.waveTargetCount = allMons.length;
|
||||
this.waveSpawnedCount = 0;
|
||||
|
||||
let assignedSlots = new Array(MissionMonCompComp.MAX_SLOTS).fill(null);
|
||||
|
||||
// 统一按顺序分配(根据所需格数找连续空位)
|
||||
for (const mon of allMons) {
|
||||
let placed = false;
|
||||
|
||||
if (mon.slotsPerMon === 3) {
|
||||
// 需要 3 格,占满连续的 3 格
|
||||
for (let idx = 0; idx < MissionMonCompComp.MAX_SLOTS - 2; idx++) {
|
||||
if (!assignedSlots[idx] && !assignedSlots[idx + 1] && !assignedSlots[idx + 2]) {
|
||||
assignedSlots[idx] = mon;
|
||||
assignedSlots[idx + 1] = "occupied"; // 占位标记
|
||||
assignedSlots[idx + 2] = "occupied"; // 占位标记
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 只需要 1 格
|
||||
for (let idx = 0; idx < MissionMonCompComp.MAX_SLOTS; idx++) {
|
||||
if (!assignedSlots[idx]) {
|
||||
assignedSlots[idx] = mon;
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!placed) {
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] No slot for monster! uuid:", mon.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
let absorbedEids = new Set<number>();
|
||||
|
||||
// 立即生成本波所有怪物
|
||||
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) {
|
||||
const req = assignedSlots[i];
|
||||
if (req && req !== "occupied") {
|
||||
let inheritedHp = 0;
|
||||
let inheritedAp = 0;
|
||||
|
||||
for (let j = 0; j < req.slotsPerMon; j++) {
|
||||
let oldEid = oldOccupiedEids[i + j];
|
||||
if (oldEid && !absorbedEids.has(oldEid)) {
|
||||
absorbedEids.add(oldEid);
|
||||
const entity = ecs.getEntityByEid(oldEid);
|
||||
if (entity) {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (attrs && attrs.hp > 0 && !attrs.is_dead) {
|
||||
inheritedHp += attrs.hp;
|
||||
inheritedAp += Math.floor(attrs.ap / 2);
|
||||
}
|
||||
entity.destroy();
|
||||
}
|
||||
}
|
||||
oldOccupiedEids[i + j] = null;
|
||||
}
|
||||
|
||||
this.addMonsterBySlot(i, req.uuid, req.isBoss, req.upType, req.monLv, req.slotsPerMon, inheritedHp, inheritedAp);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理被覆盖但没有被新怪占用槽位的旧怪(或者保留它们)
|
||||
// 按照需求,保留未被覆盖的旧怪物
|
||||
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) {
|
||||
if (oldOccupiedEids[i]) {
|
||||
const entity = ecs.getEntityByEid(oldOccupiedEids[i]!);
|
||||
if (entity) {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (attrs && attrs.hp > 0 && !attrs.is_dead) {
|
||||
this.slotOccupiedEids[i] = oldOccupiedEids[i];
|
||||
} else {
|
||||
entity.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 检查是否有任何槽位仍被活着的怪物占用 */
|
||||
private hasActiveSlotMonster() {
|
||||
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) {
|
||||
if (this.slotOccupiedEids[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新所有槽位的占用状态:
|
||||
* 检查每个占用的 ECS 实体是否仍存在且 HP > 0,
|
||||
* 已失效的清除占用标记。
|
||||
*/
|
||||
private refreshSlotOccupancy() {
|
||||
for (let i = 0; i < MissionMonCompComp.MAX_SLOTS; i++) {
|
||||
const eid = this.slotOccupiedEids[i];
|
||||
if (!eid) continue;
|
||||
const entity = ecs.getEntityByEid(eid);
|
||||
if (!entity) {
|
||||
this.slotOccupiedEids[i] = null;
|
||||
continue;
|
||||
}
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (!attrs || attrs.hp <= 0) {
|
||||
this.slotOccupiedEids[i] = null;
|
||||
}
|
||||
// 4. 立即生成本波所有怪物
|
||||
for (const req of allMons) {
|
||||
this.addMonsterAt(req.lane, this.laneIndices[req.lane], req.uuid, req.isBoss, req.upType, req.monLv);
|
||||
this.laneIndices[req.lane]++;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 怪物生成 ========================
|
||||
|
||||
/**
|
||||
* 在指定槽位生成一个怪物:
|
||||
* 1. 计算出生坐标(多格时居中)。
|
||||
* 2. 创建 Monster ECS 实体。
|
||||
* 3. 标记槽位占用。
|
||||
* 4. 设置渲染排序(Boss 优先级更高)。
|
||||
* 5. 根据阶段和成长类型计算最终 AP / HP。
|
||||
* 在指定层级、指定索引处生成一个怪物:
|
||||
*
|
||||
* @param slotIndex 槽位索引(0-5)
|
||||
* @param laneIndex 飞行层索引 (0, 1, 2)
|
||||
* @param monIndex 该层级的第几个怪 (0, 1, 2...)
|
||||
* @param uuid 怪物 UUID
|
||||
* @param isBoss 是否为 Boss
|
||||
* @param upType 属性成长类型
|
||||
* @param monLv 怪物等级
|
||||
* @param slotsPerMon 占用格数
|
||||
*/
|
||||
private addMonsterBySlot(
|
||||
slotIndex: number,
|
||||
private addMonsterAt(
|
||||
laneIndex: number,
|
||||
monIndex: number,
|
||||
uuid: number = 1001,
|
||||
isBoss: boolean = false,
|
||||
upType: UpType = UpType.AP1_HP1,
|
||||
monLv: number = 1,
|
||||
slotsPerMon: number = 1,
|
||||
inheritedHp: number = 0,
|
||||
inheritedAp: number = 0,
|
||||
monLv: number = 1
|
||||
) {
|
||||
let mon = ecs.getEntity<Monster>(Monster);
|
||||
let scale = -1;
|
||||
// 多格占用时居中出生点
|
||||
const centerXOffset = (slotsPerMon - 1) * MissionMonCompComp.MON_SLOT_X_INTERVAL / 2;
|
||||
const spawnX = MissionMonCompComp.MON_SLOT_START_X + slotIndex * MissionMonCompComp.MON_SLOT_X_INTERVAL + centerXOffset;
|
||||
const landingY = BoxSet.GAME_LINE + (isBoss ? 6 : 0);
|
||||
|
||||
// 计算坐标
|
||||
const spawnX = MissionMonCompComp.MON_SPAWN_START_X + monIndex * MissionMonCompComp.MON_SPAWN_GAP_X;
|
||||
const landingY = BoxSet.GAME_LINE + MissionMonCompComp.FLY_LANE_Y_OFFSETS[laneIndex] + (isBoss ? 6 : 0);
|
||||
const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0);
|
||||
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
|
||||
|
||||
mon.load(spawnPos, scale, uuid, isBoss, landingY, monLv);
|
||||
mon.load(spawnPos, scale, uuid, isBoss, landingY, monLv, laneIndex);
|
||||
|
||||
// 标记槽位占用
|
||||
for (let j = 0; j < slotsPerMon; j++) {
|
||||
if (slotIndex + j < MissionMonCompComp.MAX_SLOTS) {
|
||||
this.slotOccupiedEids[slotIndex + j] = mon.eid;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置渲染排序
|
||||
const move = mon.get(MoveComp);
|
||||
const move = mon.get(MonMoveComp);
|
||||
if (move) {
|
||||
move.spawnOrder = isBoss
|
||||
? MissionMonCompComp.BOSS_RENDER_PRIORITY + this.globalSpawnOrder
|
||||
: this.globalSpawnOrder;
|
||||
move.spawnOrder = this.globalSpawnOrder;
|
||||
}
|
||||
|
||||
// 计算最终属性
|
||||
@@ -511,8 +374,8 @@ export class MissionMonCompComp extends CCComp {
|
||||
const stage = this.getCurrentStage();
|
||||
const grow = this.resolveGrowPair(upType, isBoss);
|
||||
const bias = Math.max(0.1, this.getSpawnPowerBias());
|
||||
model.ap = Math.max(1, Math.floor((base.ap + stage * grow[0]) * bias)) + inheritedAp;
|
||||
model.hp_max = Math.max(1, Math.floor((base.hp + stage * grow[1]) * bias)) + inheritedHp;
|
||||
model.ap = Math.max(1, Math.floor((base.ap + stage * grow[0]) * bias));
|
||||
model.hp_max = Math.max(1, Math.floor((base.hp + stage * grow[1]) * bias));
|
||||
model.hp = model.hp_max;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user