You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
12 KiB
328 lines
12 KiB
import { EcsComponent } from 'db://assets/mx/module/ecs';
|
|
import type { EntityId } from 'db://assets/mx/module/ecs';
|
|
import type { Cell } from '../battlefield/Cell';
|
|
import type { EffectSpec } from '../config/BattleConfigTypes';
|
|
import type { MembershipSet } from '../../../tools/MembershipSet';
|
|
|
|
/** 位置组件(格子坐标) */
|
|
export class TransformComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.TransformComp';
|
|
constructor(public row = 0, public col = 0) { super(); }
|
|
}
|
|
|
|
/** 阵营组件 */
|
|
export class TeamComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.TeamComp';
|
|
constructor(public team: number, public ownerId: number) { super(); }
|
|
}
|
|
|
|
/** 生命组件 */
|
|
export class HealthComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.HealthComp';
|
|
constructor(public hp: number, public maxHp: number) { super(); }
|
|
}
|
|
|
|
/** 战斗属性组件 */
|
|
export class CombatComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.CombatComp';
|
|
constructor(
|
|
public attack: number,
|
|
public attackSpeed: number,
|
|
/** 攻击射程(像素,与配表 base_atk.range 一致;-1 全屏在写入前已展开) */
|
|
public range: number,
|
|
public critRate: number,
|
|
public critDamage: number,
|
|
/** 攻速上限(次/秒);aspd_add 加成后仍受此限。0=不限 */
|
|
public maxAttackSpeed: number = 0,
|
|
) { super(); }
|
|
}
|
|
|
|
/** 技能集组件 */
|
|
export class AbilitySetComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.AbilitySetComp';
|
|
constructor(
|
|
public abilityIds: string[],
|
|
public cooldowns: Record<string, number> = {},
|
|
) { super(); }
|
|
}
|
|
|
|
/** 占地组件 */
|
|
export class GridOccupancyComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.GridOccupancyComp';
|
|
constructor(
|
|
public anchor: Cell,
|
|
public cells: Cell[],
|
|
public blocksPath: boolean,
|
|
) { super(); }
|
|
}
|
|
|
|
/** 标签组件 */
|
|
export class TagComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.TagComp';
|
|
constructor(public tags: string[] = []) { super(); }
|
|
}
|
|
|
|
/** 单个 Buff 实例 */
|
|
export interface BuffInstance {
|
|
/** Buff 配置 ID */
|
|
buffId: string;
|
|
/** 当前层数 */
|
|
stacks: number;
|
|
/** 剩余时间(秒);次数型 Buff 为 Infinity */
|
|
remain: number;
|
|
/** 施加者实体 ID */
|
|
caster: EntityId;
|
|
/** 剩余生效次数(次数型 Buff 专用;undefined 表示非次数型) */
|
|
charges?: number;
|
|
/** tick 累计时间 */
|
|
tickAccum?: number;
|
|
}
|
|
|
|
/** Buff 容器组件 */
|
|
export class BuffContainerComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.BuffContainerComp';
|
|
constructor(public buffs: BuffInstance[] = []) { super(); }
|
|
|
|
/** MovementSystem O(1):是否定身(眩晕 / 缚仙网 / 三昧真火 / 冰冻,移速归零) */
|
|
hasDebuff4 = false;
|
|
|
|
/** buff 列表变更后同步定身标记 */
|
|
refreshDebuff4Flag(): void {
|
|
for (let i = 0; i < this.buffs.length; i++) {
|
|
const id = this.buffs[i].buffId;
|
|
if (id === 'debuff_4' || id === 'debuff_bind_net' || id === 'debuff_fire_trap' || id === 'debuff_13') {
|
|
this.hasDebuff4 = true;
|
|
return;
|
|
}
|
|
}
|
|
this.hasDebuff4 = false;
|
|
}
|
|
}
|
|
|
|
/** 单位元数据组件 */
|
|
export class UnitMetaComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.UnitMetaComp';
|
|
constructor(
|
|
public unitDefId: string,
|
|
public level = 1,
|
|
/** 出生口(WaveSpawner 出怪口编号,复活回队用) */
|
|
public spawnPoint = 0,
|
|
) { super(); }
|
|
|
|
/**
|
|
* 空投飞禽等:芭蕉扇后退/复活回收目标格(逻辑格)。
|
|
* homeRow < 0 表示未设置,仍走出怪口。
|
|
*/
|
|
public homeRow = -1;
|
|
public homeCol = -1;
|
|
/** 非空时表现层优先用此 Spine 皮肤;空串走默认阵营皮 */
|
|
public spineSkinOverride = '';
|
|
/** 从当前格抛物线跳到 path[0],不走出怪口、不沿途平移 */
|
|
public spawnJumpToPath = false;
|
|
/**
|
|
* 脚下用发光阴影(技能召唤等非基地波次)。
|
|
* false:基地出怪口波次怪用普通阴影。
|
|
*/
|
|
public glowShadow = false;
|
|
}
|
|
|
|
/** 导航/移动组件(路径由 Pathfinder 规划,位移由 MovementSystem 驱动) */
|
|
export class MovementComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.MovementComp';
|
|
/** 配表基础移速(不受减速/晕眩影响) */
|
|
public baseSpeed: number;
|
|
/** 攻击目标世界坐标(逻辑像素,与 GridMap.cellToWorld 同系) */
|
|
public goalWorldX = 0;
|
|
public goalWorldY = 0;
|
|
/** 是否已绑定攻击目标世界坐标(用于距离停步开战) */
|
|
public hasAttackGoal = false;
|
|
/** 出怪表现未完成时锁定逻辑位移(由 Presenter 解锁) */
|
|
public presentLocked = false;
|
|
/**
|
|
* 出生跳出保护:不可被索敌、不可被攻击命中。
|
|
* 跳出完成后再延时解除(由 Presenter 控制)。
|
|
*/
|
|
public spawnProtected = false;
|
|
/**
|
|
* 逻辑坐标被权威改写(击退等)后,要求表现层即使 reachedGoal 也同步一次。
|
|
* 由 MovementBridge 消费并清零。
|
|
*/
|
|
public forcePresentSync = false;
|
|
/**
|
|
* >0 时强制沿当前 path 以该速度移动(忽略攻击停步与移速同步)。
|
|
* 芭蕉扇推回等;路径走完后由 MovementSystem 清零并重寻路。
|
|
*/
|
|
public forcePathSpeed = 0;
|
|
/**
|
|
* 强制路径结束时不重寻路,保持原地(presentLocked),供芭蕉扇「退到出怪口再隐藏入队」。
|
|
*/
|
|
public forcePathHoldAtEnd = false;
|
|
/** 芭蕉扇吹退中:暂时不可被索敌/命中 */
|
|
public bajiaoBlowRetreating = false;
|
|
/** 阎王拘魂已入笼:脱离地图;伸爪阶段见 imprisonBy */
|
|
public imprisoned = false;
|
|
/** 囚禁锚点世界坐标(逻辑像素) */
|
|
public imprisonAnchorX = 0;
|
|
public imprisonAnchorY = 0;
|
|
/** 囚禁锚点参考格(牢笼所在字块格,与 imprisonAnchor 成对) */
|
|
public imprisonAnchorRow = 0;
|
|
public imprisonAnchorCol = 0;
|
|
/** 囚禁时怪物节点 Cocos worldPosition(腰点对齐牢笼锚点,非格心) */
|
|
public imprisonCocosAnchorX = 0;
|
|
public imprisonCocosAnchorY = 0;
|
|
/** 囚禁施法者实体;伸爪瞬间即设置,拖入动画期间亦归属阎王 */
|
|
public imprisonBy: EntityId | null = null;
|
|
/** 沿当前 path 到攻击点的剩余逻辑像素;无路径为 Infinity,由 MovementSystem 维护 */
|
|
public remainPathPx = Number.POSITIVE_INFINITY;
|
|
|
|
constructor(
|
|
public speed: number,
|
|
public path: Cell[] = [],
|
|
public pathIndex = 0,
|
|
public worldX = 0,
|
|
public worldY = 0,
|
|
public reachedGoal = false,
|
|
) {
|
|
super();
|
|
this.baseSpeed = speed;
|
|
}
|
|
}
|
|
|
|
/** 阎王伸爪拖入中或已囚禁:归属阎王,他人不可选/不可打 */
|
|
export function isYanSoulClaimed(move: MovementComp | undefined | null): boolean {
|
|
return move?.imprisonBy != null;
|
|
}
|
|
|
|
/** 芭蕉扇吹退中:暂时不可被索敌/命中 */
|
|
export function isBajiaoBlowRetreating(move: MovementComp | undefined | null): boolean {
|
|
return !!move?.bajiaoBlowRetreating;
|
|
}
|
|
|
|
/** 阎王归属目标是否允许本次伤害(仅囚禁施法者的 yan_cage_dot) */
|
|
export function canDealDamageToYanClaimed(
|
|
move: MovementComp | undefined | null,
|
|
caster: EntityId,
|
|
damageType?: string,
|
|
): boolean {
|
|
if (!move?.imprisonBy) return true;
|
|
return damageType === 'yan_cage_dot' && caster === move.imprisonBy;
|
|
}
|
|
|
|
/** 投射物组件 */
|
|
export class ProjectileComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.ProjectileComp';
|
|
/** 飞行模式:homing=制导追踪;linear=直线穿透 */
|
|
mode: 'homing' | 'linear';
|
|
/** 直线模式方向(格/单位,已归一化) */
|
|
direction?: { dr: number; dc: number };
|
|
/** 直线模式剩余飞行距离(格) */
|
|
travelRemaining?: number;
|
|
/** 剩余可穿透目标数(linear 模式;null=无限) */
|
|
pierceRemaining?: number | null;
|
|
/** 已命中目标集合(穿透模式去重) */
|
|
hitSet?: MembershipSet<EntityId>;
|
|
/** 表现层资源名(prefab/spine) */
|
|
prefab?: string;
|
|
|
|
constructor(
|
|
public caster: EntityId,
|
|
public speed: number,
|
|
public target: EntityId,
|
|
public hitEffects: EffectSpec[],
|
|
public damageScale: number,
|
|
public damageType?: string,
|
|
) {
|
|
super();
|
|
this.mode = 'homing';
|
|
}
|
|
}
|
|
|
|
/** 属性修改器 */
|
|
export interface AttributeModifier {
|
|
/** 目标属性名 */
|
|
attr: string;
|
|
/** 修改方式 */
|
|
op: 'Add' | 'Mul' | 'Override' | 'PostMulAdd';
|
|
/** 修改值 */
|
|
value: number;
|
|
/** 来源标识(技能/Buff ID 等) */
|
|
source: string;
|
|
/** 剩余持续时间(秒),undefined 表示永久 */
|
|
remain?: number;
|
|
/** 同 attr + 同 group 的临时正面 modifier 只保留 1 个(新替换旧);undefined 不参与替换 */
|
|
group?: string;
|
|
/** 剩余生效次数(atk_before/after 子类型3 的临时 buff 专用) */
|
|
charges?: number;
|
|
}
|
|
|
|
/** 属性组件(base + modifier) */
|
|
export class AttributeComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.AttributeComp';
|
|
constructor(
|
|
public base: Record<string, number> = {},
|
|
public modifiers: AttributeModifier[] = [],
|
|
) { super(); }
|
|
}
|
|
|
|
/** 战斗属性 ID(与 CombatComp / AttributeComp 对应) */
|
|
export enum AttrId {
|
|
/** 攻击力 */
|
|
Attack = 'attack',
|
|
/** 攻击速度 */
|
|
AttackSpeed = 'attackSpeed',
|
|
/** 攻击射程(像素) */
|
|
Range = 'range',
|
|
/** 暴击率 */
|
|
CritRate = 'critRate',
|
|
/** 暴击伤害 */
|
|
CritDamage = 'critDamage',
|
|
/** 移动速度 */
|
|
MoveSpeed = 'moveSpeed',
|
|
}
|
|
|
|
/**
|
|
* 普攻连击队列(atk_before 子类型1、次数型触发)。
|
|
* 首击命中后 pending>0;表现 finish 解锁时由 AbilitySystem 立即再施放普攻。
|
|
*/
|
|
export class ComboAttackComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.ComboAttackComp';
|
|
constructor(
|
|
public pending = 0,
|
|
public target: EntityId | null = null,
|
|
public useMaxAspd = true,
|
|
/** base_para code=6 连发间隔(秒);0 表示 finish 后立即再施放 */
|
|
public intervalSec = 0,
|
|
/** 连发目标技能;缺省时取 interruptibleByCast 普攻 */
|
|
public abilityId: string | null = null,
|
|
/** base_para code=6 连发已索敌单位(优先打不同目标) */
|
|
public usedTargets: EntityId[] = [],
|
|
/** 已预约下一段连发定时器,防止 finish 重复 flush 叠定时器 */
|
|
public burstTimerPending = false,
|
|
) { super(); }
|
|
}
|
|
|
|
/**
|
|
* 词条命中前后 hook(按普攻/大招分流)。
|
|
* base_atk 自身的 before/after 仍走 DealDamage.params。
|
|
*/
|
|
export class AtkHookComp extends EcsComponent {
|
|
static readonly ecsTypeId = 'mx.AtkHookComp';
|
|
constructor(
|
|
/** up_type=1/4 → 普攻 */
|
|
public before: string[] = [],
|
|
public after: string[] = [],
|
|
/** up_type=2/4 → 大招 */
|
|
public ultBefore: string[] = [],
|
|
public ultAfter: string[] = [],
|
|
/** skill_special=1 时大招可暴击 */
|
|
public ultCanCrit = false,
|
|
/** 受击增伤标记叠层(atk_after 子2)存在目标身上用 AttributeComp;此处仅占位扩展 */
|
|
public specials: string[] = [],
|
|
/** Skill.skill 类型8:每 distPerPx 像素加 distAddWan(万分比) */
|
|
public distPerPx = 0,
|
|
/** 每段距离的伤害加值(万分比) */
|
|
public distAddWan = 0,
|
|
/** 距离加伤上限(万分比) */
|
|
public distMaxWan = 0,
|
|
) { super(); }
|
|
}
|
|
|