fix(战斗): 修复英雄移动和施法逻辑
- 移动系统现在会在需要保持距离时也执行移动,避免过于靠近敌人 - 施法系统重构目标选择逻辑,确保在射程内寻找最近敌人 - 添加近战施法距离常量,根据英雄类型动态计算最大施法范围 - 移除不必要的攻击状态检查,优化施法条件判断
This commit is contained in:
@@ -198,8 +198,8 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
const [minRange, maxRange] = this.resolveCombatRange(model, defaultMin, defaultMax);
|
const [minRange, maxRange] = this.resolveCombatRange(model, defaultMin, defaultMax);
|
||||||
const dist = Math.abs(currentX - enemyX);
|
const dist = Math.abs(currentX - enemyX);
|
||||||
const needMoveToFormation = Math.abs(currentX - targetX) > 5;
|
const needMoveToFormation = Math.abs(currentX - targetX) > 5;
|
||||||
|
const shouldKeepDistance = dist < minRange;
|
||||||
if (dist >= minRange && needMoveToFormation) {
|
if (needMoveToFormation || shouldKeepDistance) {
|
||||||
const dir = targetX > currentX ? 1 : -1;
|
const dir = targetX > currentX ? 1 : -1;
|
||||||
move.direction = dir;
|
move.direction = dir;
|
||||||
const speed = model.speed / 3;
|
const speed = model.speed / 3;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { smc } from "../common/SingletonModuleComp";
|
|||||||
import { GameConst } from "../common/config/GameConst";
|
import { GameConst } from "../common/config/GameConst";
|
||||||
import { oops } from "db://oops-framework/core/Oops";
|
import { oops } from "db://oops-framework/core/Oops";
|
||||||
import { GameEvent } from "../common/config/GameEvent";
|
import { GameEvent } from "../common/config/GameEvent";
|
||||||
|
import { HType } from "../common/config/heroSet";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ==================== 自动施法系统 ====================
|
* ==================== 自动施法系统 ====================
|
||||||
@@ -25,6 +26,7 @@ import { GameEvent } from "../common/config/GameEvent";
|
|||||||
export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||||
debugMode: boolean = false; // 是否启用调试模式
|
debugMode: boolean = false; // 是否启用调试模式
|
||||||
private readonly emptyCastPlan = { skillId: 0, targets: [] as HeroViewComp[], isFriendly: false };
|
private readonly emptyCastPlan = { skillId: 0, targets: [] as HeroViewComp[], isFriendly: false };
|
||||||
|
private readonly meleeCastRange = 64;
|
||||||
|
|
||||||
filter(): ecs.IMatcher {
|
filter(): ecs.IMatcher {
|
||||||
return ecs.allOf(HeroAttrsComp, HeroViewComp);
|
return ecs.allOf(HeroAttrsComp, HeroViewComp);
|
||||||
@@ -38,13 +40,13 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
if (!heroAttrs || !heroView || !heroView.node) return;
|
if (!heroAttrs || !heroView || !heroView.node) return;
|
||||||
if (heroAttrs.is_dead || heroAttrs.is_reviving || heroAttrs.isStun() || heroAttrs.isFrost()) return;
|
if (heroAttrs.is_dead || heroAttrs.is_reviving || heroAttrs.isStun() || heroAttrs.isFrost()) return;
|
||||||
heroAttrs.updateCD(this.dt);
|
heroAttrs.updateCD(this.dt);
|
||||||
if (!heroAttrs.is_atking) return;
|
|
||||||
const castPlan = this.pickCastSkill(heroAttrs, heroView);
|
const castPlan = this.pickCastSkill(heroAttrs, heroView);
|
||||||
if (castPlan.skillId === 0 || castPlan.targets.length === 0) return;
|
if (castPlan.skillId === 0 || castPlan.targets.length === 0) return;
|
||||||
this.castSkill(e, castPlan, heroAttrs, heroView);
|
this.castSkill(e, castPlan, heroAttrs, heroView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { skillId: number; targets: HeroViewComp[]; isFriendly: boolean } {
|
private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { skillId: number; targets: HeroViewComp[]; isFriendly: boolean } {
|
||||||
|
const { target, inRange } = this.resolveCastTarget(heroAttrs, heroView);
|
||||||
const skillCandidates = [heroAttrs.skill_id, heroAttrs.atk_id];
|
const skillCandidates = [heroAttrs.skill_id, heroAttrs.atk_id];
|
||||||
for (const s_uuid of skillCandidates) {
|
for (const s_uuid of skillCandidates) {
|
||||||
if (!s_uuid) continue;
|
if (!s_uuid) continue;
|
||||||
@@ -56,9 +58,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
if (this.isFriendlySkill(config.TGroup)) {
|
if (this.isFriendlySkill(config.TGroup)) {
|
||||||
return { skillId: s_uuid, targets: [heroView], isFriendly: true };
|
return { skillId: s_uuid, targets: [heroView], isFriendly: true };
|
||||||
}
|
}
|
||||||
if (!heroAttrs.enemy_in_cast_range) continue;
|
if (!target || !inRange) continue;
|
||||||
const target = this.resolveCombatTarget(heroAttrs);
|
|
||||||
if (!target) continue;
|
|
||||||
return { skillId: s_uuid, targets: [target], isFriendly: false };
|
return { skillId: s_uuid, targets: [target], isFriendly: false };
|
||||||
}
|
}
|
||||||
return this.emptyCastPlan;
|
return this.emptyCastPlan;
|
||||||
@@ -177,4 +177,54 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
if (targetAttrs.fac === heroAttrs.fac) return null;
|
if (targetAttrs.fac === heroAttrs.fac) return null;
|
||||||
return targetView;
|
return targetView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resolveCastTarget(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { target: HeroViewComp | null; inRange: boolean } {
|
||||||
|
const combatTarget = this.resolveCombatTarget(heroAttrs);
|
||||||
|
if (combatTarget && this.isEnemyInCastRange(heroAttrs, heroView, combatTarget)) {
|
||||||
|
return { target: combatTarget, inRange: true };
|
||||||
|
}
|
||||||
|
const nearestInRange = this.findNearestEnemy(heroAttrs, heroView, true);
|
||||||
|
if (nearestInRange) {
|
||||||
|
return { target: nearestInRange, inRange: true };
|
||||||
|
}
|
||||||
|
const fallback = combatTarget ?? this.findNearestEnemy(heroAttrs, heroView, false);
|
||||||
|
if (!fallback) return { target: null, inRange: false };
|
||||||
|
return { target: fallback, inRange: this.isEnemyInCastRange(heroAttrs, heroView, fallback) };
|
||||||
|
}
|
||||||
|
|
||||||
|
private findNearestEnemy(heroAttrs: HeroAttrsComp, heroView: HeroViewComp, requireInRange: boolean): HeroViewComp | null {
|
||||||
|
if (!heroView.node) return null;
|
||||||
|
const currentX = heroView.node.position.x;
|
||||||
|
let nearest: HeroViewComp | null = null;
|
||||||
|
let minDist = Infinity;
|
||||||
|
ecs.query(this.filter()).forEach(entity => {
|
||||||
|
const attrs = entity.get(HeroAttrsComp);
|
||||||
|
const view = entity.get(HeroViewComp);
|
||||||
|
if (!attrs || !view?.node) return;
|
||||||
|
if (attrs.fac === heroAttrs.fac) return;
|
||||||
|
if (attrs.is_dead || attrs.is_reviving) return;
|
||||||
|
const dist = Math.abs(currentX - view.node.position.x);
|
||||||
|
if (requireInRange && !this.isEnemyInCastRange(heroAttrs, heroView, view)) return;
|
||||||
|
if (dist >= minDist) return;
|
||||||
|
minDist = dist;
|
||||||
|
nearest = view;
|
||||||
|
});
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isEnemyInCastRange(heroAttrs: HeroAttrsComp, heroView: HeroViewComp, target: HeroViewComp): boolean {
|
||||||
|
if (!heroView.node || !target.node) return false;
|
||||||
|
const dist = Math.abs(heroView.node.position.x - target.node.position.x);
|
||||||
|
const type = heroAttrs.type as HType;
|
||||||
|
const maxRange = this.resolveMaxCastRange(heroAttrs, type);
|
||||||
|
return dist <= maxRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveMaxCastRange(heroAttrs: HeroAttrsComp, type: HType): number {
|
||||||
|
const cached = heroAttrs.getCachedMaxSkillDistance();
|
||||||
|
if (cached > 0) return cached;
|
||||||
|
if (type === HType.Long) return 720;
|
||||||
|
if (type === HType.Mid) return 360;
|
||||||
|
return this.meleeCastRange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user