消消方块阵换皮表情
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.
 
 
 

324 lines
12 KiB

import { _decorator, Component, Node, Vec3, Vec2, v3, v2, CCInteger, CCFloat, Collider2D, Contact2DType, IPhysics2DContact, RigidBody2D, ERigidBody2DType, sp, instantiate, isValid, tween } from 'cc';
import { BattleDataManager, BezierFallEffectType, IWeaponProperty } from '../BattleDataManager';
import { Skill } from './Skill';
import { Monster } from '../Monster';
const { ccclass, property } = _decorator;
// 无人机状态枚举
enum DroneState {
MOVE,// 移动
ATTACKING, // 攻击中
}
@ccclass('DroneBullet')
export class DroneBullet extends Component {
@property
public moveSpeed: number = 100; // 移动速度
@property
public attackRange: number = 200; // 攻击范围
currentState: DroneState = DroneState.MOVE;
targetMonster: Node = null!;
private positionOffset: Vec3 = new Vec3(); // 用于避免重叠的偏移
private fuelCount: number = 0; // 燃油计数
_sk: sp.Skeleton = null!;
curAnimationName = ''
_dt = -1;
_attackTime = 10000;
/**存在时间 */
private existTime: number = 0;
weaponProperty: IWeaponProperty = null!;
private _targetMonsterComp: Monster | null = null;
private _offsetRecalcTimer = 0;
private _offsetRecalcInterval = 0.12;
protected onLoad(): void {
this._sk = this.node.getComponentInChildren(sp.Skeleton)
}
setData(weaponProperty: IWeaponProperty, target: Node) {
this.weaponProperty = weaponProperty;
this.targetMonster = target;
this._targetMonsterComp = this.targetMonster?.getComponent(Monster) || null;
this.currentState = DroneState.MOVE;
this._attackTime = weaponProperty.damageInterval;
this.existTime = weaponProperty.weaponExistTime;
this.playEffect("2无人机原地飞");
}
/**
* 播放 Spine 动画
* @param name 动画名称
* @param callback 动画播放完成后的回调函数
* @param timeScale 动画播放速度倍率
* @param loop 是否循环播放
*/
private playSpine(name: string, callback: Function = null, timeScale = 1, loop = true) {
// 如果节点不存在,则直接返回
if (this.node == null) return;
// 设置动画播放完成后的回调函数
// this._sk.setCompleteListener(() => {
// if (callback) callback();
// });
// 设置动画播放速度倍率,考虑了战斗速度的影响
// this._sk.timeScale = timeScale * 1//BattleManager.Instance.LevelBattle.BattleSpeed;
// 播放指定名称的动画,并设置是否循环播放
this._sk.setAnimation(0, name, loop);
}
playEffect(name) {
let nameStr = name
//nameStr = '1无人机漂浮'
// if (this.currentState == DroneState.ATTACKING) {
// } else {
// nameStr = '2无人机原地飞'
// }
if (this.curAnimationName != nameStr) {
this.curAnimationName = nameStr
this.playSpine(nameStr)
}
}
update(deltaTime: number) {
if (!gg.game.CurentBattle.canUpdateFrame()) return;
const scaledDt = deltaTime * gg.game.CurentBattle.TimeScale;
this.existTime -= scaledDt;
if (this.existTime <= 0) {
this.remove();
return;
}
switch (this.currentState) {
case DroneState.MOVE:
if (!this.targetMonster || !isValid(this.targetMonster)) {
const newTarget = this.findTarget();
if (newTarget) {
this.targetMonster = newTarget;
this._targetMonsterComp = newTarget.getComponent(Monster);
} else {
// 没找到目标时保持待机,直到存在时间结束
this.playEffect("2无人机原地飞");
break;
}
}
if (this.moveToTarget(scaledDt)) {
this.currentState = DroneState.ATTACKING;
this.updatePositionOffset(); // 攻击开始时先拉一次偏移
this._offsetRecalcTimer = 0;
this.playEffect("1无人机漂浮");
}
break;
case DroneState.ATTACKING:
this.updateAttacking(scaledDt);
// updateAttacking 里可能因目标死亡/丢失切回 MOVE;该帧不应继续扣计时并提前开火
if (this.currentState !== DroneState.ATTACKING) {
break;
}
this._attackTime -= scaledDt;
if (this._attackTime <= 0) {
this.attackTarget();
}
break;
}
}
// 寻找攻击范围内的怪物
private findTarget() {
let monster = gg.game.CurentBattle.MonsterSpawner.getDroneTargetMonster(this.node, this.attackRange);
return monster;
}
// 检查攻击同一目标的其他无人机
private getSameTargetDrones(): DroneBullet[] {
if (!this.targetMonster) return [];
const allDrones = this.node.parent.getComponentsInChildren(DroneBullet) || [];
return allDrones.filter(drone =>
drone.targetMonster === this.targetMonster &&
(drone.currentState === DroneState.ATTACKING)
);
}
// 更新位置偏移以避免重叠
private updatePositionOffset() {
const sameTargetDrones = this.getSameTargetDrones();
const totalCount = sameTargetDrones.length + 1; // 包括自己
if (totalCount <= 1) {
this.positionOffset.set(0, 0, 0);
return;
}
// 计算偏移角度,形成圆形分布
const index = sameTargetDrones.indexOf(this);
// const angle = (index + 1) * (Math.PI * 2 / totalCount);
const offsetDistance = 50; // 偏移距离,小于无人机一半大小(25)
let x = -(totalCount - 1) * offsetDistance / 2 + index * offsetDistance;
this.positionOffset.set(x, 0, 0);
// this.positionOffset.set(
// Math.cos(angle) * offsetDistance,
// Math.sin(angle) * offsetDistance,
// 0
// );
}
// 移动到目标位置
private moveToTarget(deltaTime: number): boolean {
if (!this.targetMonster || !isValid(this.targetMonster)) return false;
const tp = this.targetMonster.position;
const tx = tp.x + 50 + this.positionOffset.x;
const ty = tp.y + 180 + this.positionOffset.y;
const tz = tp.z + this.positionOffset.z;
const cp = this.node.position;
const cx = cp.x;
const cy = cp.y;
const cz = cp.z;
const dx = tx - cx;
const dy = ty - cy;
const dz = tz - cz;
const distSq = dx * dx + dy * dy + dz * dz;
const moveStep = this.moveSpeed * deltaTime;
const moveStepSq = moveStep * moveStep;
if (distSq <= moveStepSq) {
this.node.setPosition(tx, ty, tz);
return true;
}
const dist = Math.sqrt(distSq);
const ratio = moveStep / dist;
this.node.setPosition(cx + dx * ratio, cy + dy * ratio, cz + dz * ratio);
return false;
}
// 攻击状态更新
private updateAttacking(deltaTime: number) {
if (!this.targetMonster || !this.targetMonster.isValid) {
// 目标已消失,检查新目标
const newTarget = this.findTarget();
if (newTarget) {
this.targetMonster = newTarget;
this._targetMonsterComp = newTarget.getComponent(Monster);
this.currentState = DroneState.MOVE;
this._attackTime = this.weaponProperty?.damageInterval ?? this._attackTime;
} else {
// 不立即消失,回到移动态继续等待/搜敌
this.targetMonster = null;
this._targetMonsterComp = null;
this.currentState = DroneState.MOVE;
this.playEffect("2无人机原地飞");
}
return;
}
if (!this._targetMonsterComp) this._targetMonsterComp = this.targetMonster.getComponent(Monster);
if (this._targetMonsterComp?.IsDead) {
const newTarget = this.findTarget();
if (newTarget) {
this.targetMonster = newTarget;
this._targetMonsterComp = newTarget.getComponent(Monster);
this.currentState = DroneState.MOVE;
this._attackTime = this.weaponProperty?.damageInterval ?? this._attackTime;
} else {
this.targetMonster = null;
this._targetMonsterComp = null;
this.currentState = DroneState.MOVE;
this.playEffect("2无人机原地飞");
}
return;
}
// 保持在目标上方
this._offsetRecalcTimer += deltaTime;
if (this._offsetRecalcTimer >= this._offsetRecalcInterval) {
this._offsetRecalcTimer = 0;
this.updatePositionOffset();
}
const tp = this.targetMonster.position;
this.node.setPosition(
tp.x + 50 + this.positionOffset.x,
tp.y + 180 + this.positionOffset.y,
tp.z + this.positionOffset.z
);
}
// 攻击目标
public attackTarget() {
if (this.targetMonster && this.targetMonster.isValid) {
if (!this._targetMonsterComp) this._targetMonsterComp = this.targetMonster.getComponent(Monster);
if (!this._targetMonsterComp || this._targetMonsterComp.IsDead) return;
// console.log("攻击目标attackTarget");
//if (Vec3.distance(this.node, this.targetMonster) > this.attackRange) return;
this._attackTime = this.weaponProperty.damageInterval;
// 节流交给 SoundManager(按音效名);多无人机勿抢同一本地锁挡掉射击声
gg.audio.playEffect({ name: '无人机攻击', path: 'sound/武器音效/' });
let isFuelBomb = false
//特殊技能(丢燃烧弹)
let weaponPropertyData = this.node.getComponent(Skill).weaponPropertyData;
let specialSkillAddFallEffectInfo = weaponPropertyData.specialSkillAddFallEffectInfo
for (let i = 0; i < specialSkillAddFallEffectInfo.length; i++) {
let buffInfo = specialSkillAddFallEffectInfo[i]
let arr = buffInfo.split(',')
let buffType = Number(arr[0])
//落地添加燃烧区域
if (buffType == BezierFallEffectType.fallBombBurnArea) {
let atkCount = Number(arr[7])//多少次普攻发射一次燃油弹
this.fuelCount++
if (this.fuelCount >= atkCount) {
isFuelBomb = true
this.fuelCount = 0;
this.executeDroneWeaponSkill(true)
break
}
}
}
if (!isFuelBomb) {
this.executeDroneWeaponSkill(false)
}
}
}
/**
* 执行武器技能逻辑(无人机)
* @param weaponId 武器ID
* @param startPos 起始位置
* @param isFuel 是否是燃油弹
*/
executeDroneWeaponSkill(isFuel: boolean) {
const weaponState = this.getComponent(Skill).weaponData;
const startPos = this.node.find("bulletLaunchNode").worldPosition.clone().add(new Vec3(0, -50, 0));
const targetPos = this.targetMonster.position.clone();
if (!weaponState) return;
let bulletNode = this.node.find("bullet")
let weaponNode = instantiate(bulletNode);
weaponNode.setParent(gg.game.CurentBattle.SkillController.node);
weaponNode.active = true;
weaponNode.worldPosition = startPos
let skillComp = weaponNode.getComponent(Skill);
skillComp.setData(weaponState, targetPos, false, isFuel);
}
remove() {
this.currentState = DroneState.MOVE;
this.existTime = 100000;
this._attackTime = 100000;
this.targetMonster = null;
tween(this.node).to(0.5, { opacity: 0 })
.call(() => {
gg.res.putNode(this.node);
})
.start()
}
}