228 lines
7.0 KiB
TypeScript
228 lines
7.0 KiB
TypeScript
import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework';
|
|
import { HealthComponent } from '../components/HealthComponent';
|
|
|
|
/**
|
|
* 生命值系统 - 处理生命值相关的逻辑
|
|
*
|
|
* 展示生命值管理:
|
|
* 1. 自动回血
|
|
* 2. 无敌状态管理
|
|
* 3. 死亡处理
|
|
* 4. 事件触发
|
|
*/
|
|
export class HealthSystem extends EntitySystem {
|
|
/** 回血延迟时间(受伤后多久开始回血,毫秒) */
|
|
private regenDelay: number = 3000;
|
|
|
|
constructor() {
|
|
// 只处理拥有HealthComponent的实体
|
|
super(Matcher.empty().all(HealthComponent));
|
|
}
|
|
|
|
public initialize(): void {
|
|
super.initialize();
|
|
console.log("HealthSystem 已初始化 - 开始处理生命值逻辑");
|
|
}
|
|
|
|
/**
|
|
* 每帧处理:更新生命值相关逻辑
|
|
*/
|
|
protected process(entities: Entity[]): void {
|
|
for (const entity of entities) {
|
|
const health = entity.getComponent(HealthComponent);
|
|
|
|
// 处理无敌状态
|
|
this.processInvincibility(health);
|
|
|
|
// 处理生命值回复
|
|
this.processHealthRegeneration(entity, health);
|
|
|
|
// 检查死亡状态
|
|
this.checkDeathStatus(entity, health);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理无敌状态
|
|
*/
|
|
private processInvincibility(health: HealthComponent): void {
|
|
if (health.invincible && health.invincibleDuration > 0) {
|
|
health.invincibleDuration -= Time.deltaTime;
|
|
|
|
// 无敌时间结束
|
|
if (health.invincibleDuration <= 0) {
|
|
health.invincible = false;
|
|
health.invincibleDuration = 0;
|
|
console.log("无敌状态结束");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理生命值回复
|
|
*/
|
|
private processHealthRegeneration(entity: Entity, health: HealthComponent): void {
|
|
// 如果已经满血或者没有回复速度,则不处理
|
|
if (health.isFullHealth() || health.regenRate <= 0) {
|
|
return;
|
|
}
|
|
|
|
// 检查是否超过了回血延迟时间
|
|
const currentTime = Date.now();
|
|
if (currentTime - health.lastDamageTime < this.regenDelay) {
|
|
return;
|
|
}
|
|
|
|
// 计算回血量
|
|
const regenAmount = health.regenRate * Time.deltaTime;
|
|
const oldHealth = health.currentHealth;
|
|
|
|
// 执行回血
|
|
health.heal(regenAmount);
|
|
|
|
// 如果实际回了血,输出日志
|
|
if (health.currentHealth > oldHealth) {
|
|
console.log(`${entity.name} 回血: ${oldHealth.toFixed(1)} -> ${health.currentHealth.toFixed(1)} (${health.getHealthPercentage() * 100}%)`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查死亡状态
|
|
*/
|
|
private checkDeathStatus(entity: Entity, health: HealthComponent): void {
|
|
if (health.isDead()) {
|
|
this.handleEntityDeath(entity, health);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理实体死亡
|
|
*/
|
|
private handleEntityDeath(entity: Entity, health: HealthComponent): void {
|
|
console.log(`💀 ${entity.name} 已死亡!`);
|
|
|
|
// 触发死亡事件(如果有事件系统)
|
|
this.triggerDeathEvent(entity);
|
|
|
|
// 可以在这里添加死亡效果、掉落物品等逻辑
|
|
this.createDeathEffect(entity);
|
|
|
|
// 标记实体为死亡状态(而不是立即销毁)
|
|
// 这样其他系统可以处理死亡相关的逻辑
|
|
entity.addComponent(new DeadMarkerComponent());
|
|
|
|
// 可选:延迟销毁实体
|
|
setTimeout(() => {
|
|
if (entity && !entity.isDestroyed) {
|
|
entity.destroy();
|
|
console.log(`${entity.name} 已被销毁`);
|
|
}
|
|
}, 1000); // 1秒后销毁
|
|
}
|
|
|
|
/**
|
|
* 触发死亡事件
|
|
*/
|
|
private triggerDeathEvent(entity: Entity): void {
|
|
// 如果项目中有事件系统,可以在这里发送死亡事件
|
|
console.log(`触发死亡事件: ${entity.name}`);
|
|
|
|
// 示例事件数据
|
|
const deathEventData = {
|
|
entityId: entity.id,
|
|
entityName: entity.name,
|
|
deathTime: Date.now(),
|
|
position: this.getEntityPosition(entity)
|
|
};
|
|
|
|
// 这里可以调用事件系统发送事件
|
|
// eventBus.emit('entity:died', deathEventData);
|
|
}
|
|
|
|
/**
|
|
* 创建死亡效果
|
|
*/
|
|
private createDeathEffect(entity: Entity): void {
|
|
console.log(`💥 为 ${entity.name} 创建死亡效果`);
|
|
|
|
// 在实际游戏中,这里可能会:
|
|
// 1. 播放死亡动画
|
|
// 2. 播放死亡音效
|
|
// 3. 创建粒子效果
|
|
// 4. 掉落物品
|
|
}
|
|
|
|
/**
|
|
* 获取实体位置(辅助方法)
|
|
*/
|
|
private getEntityPosition(entity: Entity): { x: number; y: number; z: number } {
|
|
// 尝试获取位置组件
|
|
const position = entity.getComponent(PositionComponent);
|
|
if (position) {
|
|
return {
|
|
x: position.position.x,
|
|
y: position.position.y,
|
|
z: position.position.z
|
|
};
|
|
}
|
|
|
|
return { x: 0, y: 0, z: 0 };
|
|
}
|
|
|
|
/**
|
|
* 公共方法:对实体造成伤害
|
|
* 这个方法可以被其他系统调用
|
|
*/
|
|
public damageEntity(entity: Entity, damage: number, source?: Entity): boolean {
|
|
const health = entity.getComponent(HealthComponent);
|
|
if (!health || health.invincible) {
|
|
return false; // 无生命值组件或处于无敌状态
|
|
}
|
|
|
|
const oldHealth = health.currentHealth;
|
|
health.takeDamage(damage);
|
|
|
|
console.log(`⚔️ ${entity.name} 受到 ${damage} 点伤害: ${oldHealth.toFixed(1)} -> ${health.currentHealth.toFixed(1)}`);
|
|
|
|
// 如果有伤害来源,可以记录或处理
|
|
if (source) {
|
|
console.log(`伤害来源: ${source.name}`);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 公共方法:治疗实体
|
|
*/
|
|
public healEntity(entity: Entity, healAmount: number): boolean {
|
|
const health = entity.getComponent(HealthComponent);
|
|
if (!health || health.isFullHealth()) {
|
|
return false;
|
|
}
|
|
|
|
const oldHealth = health.currentHealth;
|
|
health.heal(healAmount);
|
|
|
|
console.log(`💚 ${entity.name} 恢复 ${healAmount} 点生命值: ${oldHealth.toFixed(1)} -> ${health.currentHealth.toFixed(1)}`);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 死亡标记组件 - 标记已死亡的实体
|
|
* 这是一个简单的标记组件,用于标识死亡状态
|
|
*/
|
|
class DeadMarkerComponent extends Component {
|
|
public deathTime: number;
|
|
|
|
constructor() {
|
|
super();
|
|
this.deathTime = Date.now();
|
|
}
|
|
}
|
|
|
|
// 导入位置组件(用于获取实体位置)
|
|
import { PositionComponent } from '../components/PositionComponent';
|
|
import { Component } from '@esengine/ecs-framework';
|