feat(技能系统): 添加治疗和护盾技能支持

实现治疗和护盾技能的基础功能,包括:
1. 在SACastSystem中添加对治疗和护盾技能类型的支持
2. 新增技能目标选择逻辑,根据技能类型选择敌人或友军
3. 添加buff动画效果和技能提示
4. 更新刘邦的技能配置为护盾技能
5. 移除不再使用的EndAnm相关文件
This commit is contained in:
walkpan
2025-12-31 19:50:27 +08:00
parent 35b677ec7a
commit beb0f9feb4
10 changed files with 204 additions and 88 deletions

View File

@@ -29,10 +29,13 @@
},
{
"__id__": 10
},
{
"__id__": 12
}
],
"_prefab": {
"__id__": 12
"__id__": 14
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -259,6 +262,24 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "c6LOemuvJKyYCqlF/yUJcr"
},
{
"__type__": "0f3c4JhFbFO2rEFqBJJ7hFv",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 13
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "5dZdUy5cVPjLHtC1SlLIIB"
},
{
"__type__": "cc.PrefabInfo",
"root": {

View File

@@ -185,7 +185,7 @@ export const SkillSet: Record<number, SkillConfig> = {
buffs:[],neAttrs:[],info:"治疗自己,回复30%最大生命值",
},
6101:{
uuid:6101,name:"魔法盾",sp_name:"buff_wind",icon:"3036",TGroup:TGroup.Self,SType:SType.buff,act:"buff",DTType:DTType.single,DType:DType.WIND,
uuid:6101,name:"魔法盾",sp_name:"buff_wind",icon:"3036",TGroup:TGroup.Self,SType:SType.shield,act:"buff",DTType:DTType.single,DType:DType.WIND,
ap:30,map:0,cd:1,t_num:1,hit_num:1,hit:1,hitcd:0.2,speed:720,cost:0,with:0,dis:80,ready:0,EAnm:0,DAnm:9001,RType:RType.fixed,EType:EType.animationEnd,
buffs:[],neAttrs:[],info:"获得30%最大生命值的护盾",
}

View File

@@ -110,7 +110,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 刘邦 - 领导型战士(善于用人,知人善任)
5001:{uuid:5001,name:"刘邦",path:"hk1", fac:FacSet.HERO, kind:1,as:1.5,
type:HType.warrior,lv:1,hp:1000,mp:85,map:10,def:9,mdef:0,ap:15,dis:100,speed:120,skills:[6002,6001],
type:HType.warrior,lv:1,hp:1000,mp:85,map:10,def:9,mdef:0,ap:15,dis:100,speed:120,skills:[6002,6101],
buff:[],tal:[7101,7201,7301],info:"楚汉争霸领袖,领导统御型战士"},
// 荆轲 - 刺客(敏捷型,高速度和暴击率)

View File

@@ -76,6 +76,9 @@ export class HeroSpine extends Component {
do_buff(){
this.anm.buff()
}
buff(){
this.anm.buff()
}
move(){
// console.log("change to move",this.status);
if(this.status=="move") return

View File

@@ -438,6 +438,10 @@ export class HeroViewComp extends CCComp {
case "atk":
this.as.atk()
break
case "buff":
this.as.buff()
this.tooltip(TooltipTypes.skill, skill.name)
break
}
}

View File

@@ -49,20 +49,24 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
const readySkills = skills.getReadySkills(heroAttrs.mp);
if (readySkills.length === 0) return;
// 选择第一个可施放的伤害技能
for (const s_uuid of readySkills) {
// 选择第一个可施放的技能(支持伤害/治疗/护盾)
for (const s_uuid of readySkills) {
const skill = skills.getSkill(s_uuid);
if (!skill) continue;
if (skill.hset === HSSet.max && !skills.max_auto) continue;
const config = SkillSet[skill.s_uuid];
if (!config || config.SType !== SType.damage) continue;
if (!config) continue;
// 检查是否有敌人在技能攻击范围内
if (!this.hasEnemyInSkillRange(heroView, heroAttrs, skill.dis)) continue;
// 根据技能类型检查目标
if (config.SType === SType.damage) {
if (!this.hasEnemyInSkillRange(heroView, heroAttrs, skill.dis)) continue;
} else if (config.SType === SType.heal || config.SType === SType.shield) {
if (!this.hasAllyInSkillRange(heroView, heroAttrs, skill.dis)) continue;
}
// ✅ 开始执行施法
this.startCast(e,skill,skill.hset);
this.startCast(e, skill, skill.hset);
// 一次只施放一个技能
break;
@@ -151,8 +155,15 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
if (hset === HSSet.max) talComp.updateCur(TriType.MAX);
}
/**********************天赋处理*************************************************************************/
// 获取目标位置
let targets = this.sTargets(heroView, s_uuid);
// 根据技能类型执行不同逻辑
if (config.SType === SType.heal) {
return this.executeHealSkill(casterEntity, s_uuid, heroView, hset);
} else if (config.SType === SType.shield) {
return this.executeShieldSkill(casterEntity, s_uuid, heroView, hset);
}
// 获取目标位置(伤害技能)
let targets = this.sTargets(heroView, s_uuid);
if (targets.length === 0) {
console.warn("[SACastSystem] 没有找到有效目标");
return false;
@@ -348,6 +359,159 @@ export class SACastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
});
return found;
}
/**
* 检查技能范围内是否有友军
*/
private hasAllyInSkillRange(heroView: HeroViewComp, heroAttrs: HeroAttrsComp, skillDistance: number): boolean {
if (!heroView || !heroView.node) return false;
const currentPos = heroView.node.position;
const team = heroAttrs.fac;
let found = false;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Math.abs(currentPos.x - view.node.position.x);
if (model.fac === team && !model.is_dead) {
if (distance <= skillDistance) {
found = true;
return true;
}
}
});
return found;
}
/**
* 执行治疗技能
*/
private executeHealSkill(casterEntity: ecs.Entity, s_uuid: number, heroView: HeroViewComp, hset: HSSet): boolean {
const heroAttrs = casterEntity.get(HeroAttrsComp);
const config = SkillSet[s_uuid];
if (!config) return false;
const targets = this.sHealTargets(heroView, heroAttrs, config);
if (targets.length === 0) return false;
const healAmount = config.ap;
const delay = 0.3;
heroView.scheduleOnce(() => {
for (const targetEntity of targets) {
const targetAttrs = targetEntity.get(HeroAttrsComp);
const targetView = targetEntity.get(HeroViewComp);
if (!targetAttrs || !targetView) continue;
targetAttrs.add_hp(healAmount, false);
targetView.health(healAmount);
}
}, delay);
return true;
}
/**
* 执行护盾技能
*/
private executeShieldSkill(casterEntity: ecs.Entity, s_uuid: number, heroView: HeroViewComp, hset: HSSet): boolean {
const heroAttrs = casterEntity.get(HeroAttrsComp);
const config = SkillSet[s_uuid];
if (!config) return false;
const targets = this.sShieldTargets(heroView, heroAttrs, config);
if (targets.length === 0) return false;
const shieldAmount = config.ap;
const delay = 0.3;
heroView.scheduleOnce(() => {
for (const targetEntity of targets) {
const targetAttrs = targetEntity.get(HeroAttrsComp);
const targetView = targetEntity.get(HeroViewComp);
if (!targetAttrs || !targetView) continue;
targetAttrs.add_shield(shieldAmount, false);
targetView.add_shield(shieldAmount);
}
}, delay);
return true;
}
/**
* 选择治疗目标
*/
private sHealTargets(caster: HeroViewComp, heroAttrs: HeroAttrsComp, config: any): ecs.Entity[] {
const targets: ecs.Entity[] = [];
const maxTargets = Math.max(1, Number(config.t_num ?? 1));
const range = Number(config.dis ?? 300);
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!model || !view || !view.node) return;
if (model.fac !== heroAttrs.fac) return;
if (model.is_dead) return;
const distance = Math.abs(caster.node.position.x - view.node.position.x);
if (distance <= range) {
targets.push(e);
}
});
targets.sort((a, b) => {
const attrsA = a.get(HeroAttrsComp);
const attrsB = b.get(HeroAttrsComp);
if (!attrsA || !attrsB) return 0;
return attrsA.hp - attrsB.hp;
});
return targets.slice(0, maxTargets);
}
/**
* 选择护盾目标
*/
private sShieldTargets(caster: HeroViewComp, heroAttrs: HeroAttrsComp, config: any): ecs.Entity[] {
const targets: ecs.Entity[] = [];
const maxTargets = Math.max(1, Number(config.t_num ?? 1));
const range = Number(config.dis ?? 300);
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).forEach(e => {
const model = e.get(HeroAttrsComp);
const view = e.get(HeroViewComp);
if (!model || !view || !view.node) return;
if (model.fac !== heroAttrs.fac) return;
if (model.is_dead) return;
const distance = Math.abs(caster.node.position.x - view.node.position.x);
if (distance <= range) {
targets.push(e);
}
});
return targets.slice(0, maxTargets);
}
/**
* 根据位置查找实体
*/
private findEntityAtPosition(pos: Vec3): ecs.Entity | null {
let foundEntity: ecs.Entity | null = null;
ecs.query(ecs.allOf(HeroAttrsComp, HeroViewComp)).some(e => {
const view = e.get(HeroViewComp);
if (!view || !view.node) return false;
const distance = Vec3.distance(pos, view.node.position);
if (distance < 50) {
foundEntity = e;
return true;
}
return false;
});
return foundEntity;
}
}

View File

@@ -1,34 +0,0 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
/** EndAnm 模块 */
@ecs.register(`EndAnm`)
export class EndAnm extends ecs.Entity {
/** ---------- 数据层 ---------- */
// EndAnmModel!: EndAnmModelComp;
/** ---------- 业务层 ---------- */
// EndAnmBll!: EndAnmBllComp;
/** ---------- 视图层 ---------- */
// EndAnmView!: EndAnmViewComp;
/** 实始添加的数据层组件 */
protected init() {
// this.addComponents<ecs.Comp>();
}
/** 模块资源释放 */
destroy() {
// 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
super.destroy();
}
}
/** EndAnm 模块业务逻辑系统组件,如无业务逻辑处理可删除此对象 */
export class EcsEndAnmSystem extends ecs.System {
constructor() {
super();
// this.add(new ecs.ComblockSystem());
}
}

View File

@@ -1,9 +0,0 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "1262d5a7-b1a1-49dc-9715-b7e28619304f",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -1,24 +0,0 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { smc } from "../common/SingletonModuleComp";
/** 业务层对象 */
@ecs.register('EndAnmCom')
export class EndAnmComComp extends ecs.Comp {
/** 业务层组件移除时,重置所有数据为默认值 */
reset() {
}
}
/** 业务层业务逻辑处理对象 */
export class EndAnmComSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
filter(): ecs.IMatcher {
return ecs.allOf(EndAnmComComp);
}
entityEnter(e: ecs.Entity): void {
// 注:自定义业务逻辑
if(!smc.mission.play || smc.mission.pause) return;
e.remove(EndAnmComComp);
}
}

View File

@@ -1,9 +0,0 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "e0728072-f94e-4741-b172-4157e7a3b335",
"files": [],
"subMetas": {},
"userData": {}
}