using System; using System.Collections.Generic; using System.Text; using CommonAI.RTS; using CommonLang.Vector; using CommonAI.ZoneClient; using CommonLang; using CommonAI.Zone.Formula; using CommonAI.Zone.Helper; using static CommonAI.Zone.Instance.InstanceUnit; using CommonAI.ZoneServer.JSGModule; using CommonAI.Data; namespace CommonAI.Zone.Instance { public class InstanceSpell : InstanceZoneObject { readonly protected SpellTemplate mInfo; readonly protected SyncSpellInfo mSyncInfo; readonly protected LaunchSpell mLaunchData; // 释放者 readonly protected InstanceUnit mLauncherUnit; // 发出者 readonly protected InstanceZoneObject mSender; protected AttackRangeHelper AttackRange; protected Vector2 mStartPos; protected float mLaunchDirection; // 被跟踪目标 protected InstanceUnit mTarget; protected Vector2 mTargetPos; protected TimeExpire<int> mSeekingCooldownTime; protected SpellChainLevelInfo mChainInfo; /// <summary> /// 如果是技能产生的伤害,就是该技能的类型(就算是法术,也要追根到技能) /// </summary> public readonly XmdsSkillType FromSkillType = XmdsSkillType.none; private float mBaseSize; private float mDistance; private float mSpeed; private PopupKeyFrames<SpellTemplate.KeyFrame> mKeyFrames; private Dictionary<uint, InstanceUnit> mHittedUnits; private bool Finish = false; private TimeInterval<SpellTemplate.KeyFrame> mHitIntervalTicker; private InstanceUnit.StateSkill mBindingSkill; private bool mClientVisible; private bool mSyncPos; private float mMoveDistance; //服务器专用的扩展字段 private int actionIndex; private int mMaxAffectUnit; //法术创建时,玩家的AOI状态 private byte aoiFlag = 0; //每隔N秒触发,触发次数 private int mHitTotalTimes; //法术总时长 private readonly int mSpellTotalTime; //法术的总时长 // 飞弹各种奇葩数据记录, 法术数量可控 private JSGCreateSpellData mCreateSpellData; //法术创建信息(可变的,由技能数据确定) // 飞弹不同的法术id public List<LaunchSpell> mCurveSpellList; //连续飞弹 private byte mCurveSpellIndex; private TVector2 mPrvePos = new TVector2(); public InstanceSpell( InstanceZone zone, SpellTemplate data, LaunchSpell launch, InstanceUnit launcher, InstanceZoneObject sender, Dictionary<uint, InstanceUnit> damageList, XmdsSkillType fromSkillType = XmdsSkillType.none, int spellIndex = 0, JSGCreateSpellData createData = null) : base(zone, false) { this.mSpellTotalTime = data.LifeTimeMS; this.mCurveSpellIndex = (byte)spellIndex; SenderID = 0; if (sender != null) { if (sender is InstanceUnit) SenderID = sender.ID; else if (sender is InstanceSpell) SenderID = (sender as InstanceSpell).SenderID; } //记录AOI状态 if (sender.AoiStatus != null) { this.aoiFlag = 1; } this.FromSkillType = fromSkillType; this.mLaunchData = launch; if (createData == null) { this.mCreateSpellData = new JSGCreateSpellData(); this.mCreateSpellData.mMaxSpellCount = launch.Count; } else { this.mCreateSpellData = createData; } this.mKeyFrames = new PopupKeyFrames<SpellTemplate.KeyFrame>(); this.mKeyFrames.AddRange(data.KeyFrames); this.mInfo = data; this.mLauncherUnit = launcher; this.mSender = sender; this.mBaseSize = data.BodySize; this.mDistance = data.Distance; this.faceTo(launcher.Direction); this.IsAffectNearChange = false; this.mSyncInfo = new SyncSpellInfo(zone.IsHalfSync); this.mSyncInfo.TemplateID = mInfo.ID; this.mSyncInfo.Force = launcher.Force; if (mInfo.HitIntervalMS <= 0) { this.mHittedUnits = (damageList == null ? new Dictionary<uint, InstanceUnit>() : damageList); } this.mClientVisible = mInfo.ClientVisible; this.mSyncPos = true;// zone.SyncPos; this.mHitIntervalTicker = new TimeInterval<SpellTemplate.KeyFrame>(data.HitIntervalMS); this.mHitIntervalTicker.Tag = data.HitIntervalKeyFrame; this.mHitTotalTimes = 0; this.AttackRange = new AttackRangeHelper(launcher); this.mSpeed = mInfo.MSpeedSEC; this.mMoveDistance = 0; if (launcher.AoiStatus != null) { this.setAoiStatus(launcher.AoiStatus); } } public SpellTemplate Info { get { return mInfo; } } public LaunchSpell LaunchData { get { return mLaunchData; } } /// <summary> /// 最先技能的发起者 /// </summary> public InstanceUnit Launcher { get { return mLauncherUnit; } } public uint LauncherID { get { return mLauncherUnit.ID; } } /// <summary> /// 技能的出口,比如技能触发技能,则Sender就是一个Spell /// </summary> public InstanceZoneObject Sender { get { return mSender; } } public InstanceUnit Target { get { return mTarget; } } public override bool IntersectMap { get { return false; } } public override bool IntersectObj { get { return false; } } public override bool Moveable { get { return true; } } public override float BodyBlockSize { get { return mBaseSize; } } public override float BodyHitSize { get { return mBaseSize; } } public override float BodyHeight { get { return mInfo.BodySize; } } public override float Weight { get { return 0; } } public override bool ClientVisible { get { return mClientVisible; } } public override bool SyncPos { get { return mSyncPos; } } public int BindActionIndex { get { return actionIndex; } set { this.actionIndex = value; } } public int MaxAffectUnit { get { return mMaxAffectUnit; } set { this.mMaxAffectUnit = value; } } /** 获取触发次数 */ public int GetHitTimes() { return this.mHitTotalTimes; } /// <summary> /// 连锁等级 /// </summary> public int ChainLevel { get { return (mChainInfo != null) ? mChainInfo.Level : 0; } } public SpellChainLevelInfo ChainInfo { get { return mChainInfo; } } public uint SenderID; public uint TargetID { get { return (mTarget != null) ? mTarget.ID : 0; } } public Vector2 TargetPos { get { return mTargetPos; } } public float LaunchHeightZ { get { if (mSender is InstanceSpell spell && spell.mInfo.MType == SpellTemplate.MotionType.Cannon) { return 0; } if (mLaunchData.FromUnitBody) { //TODO: 此时的Z并不是spell的实际高度位置 return mSender.Z; } else { return mLaunchData.LaunchSpellHeight; } } } public override SyncObjectInfo GenSyncInfo(bool net) { return GenSyncSpellInfo(net); } public SyncSpellInfo GenSyncSpellInfo(bool net = false) { mSyncInfo.x = X; mSyncInfo.y = Y; mSyncInfo.direction = this.Direction; mSyncInfo.HasSpeed = mInfo.IsMoveable && mSpeed != mInfo.MSpeedSEC; mSyncInfo.CurSpeed = mSpeed; return mSyncInfo; } // 如果是导弹类型,则需要目标 public void setTarget(InstanceUnit target) { this.mTarget = target; } public void setTargetPos(Vector2 targetPos) { this.mTargetPos = targetPos; } public void setChainInfo(SpellChainLevelInfo c) { this.mChainInfo = c; } protected override void onAdded(bool pointLv) { if (mLaunchData.IsAutoSeekingTarget && mLaunchData.PType != LaunchSpell.PosType.POS_TYPE_AREA) { //目标不存在,或者已死亡才重新寻找, 不能打开,弹射逻辑 //if(mTarget == null || mTarget.IsDead()) { mTarget = Parent.SeekSpellAttackable(this.Launcher, this.Info, this.Launcher.AoiStatus, X, Y, mLaunchData.SeekingTargetRange, Info.ExpectTarget, mLaunchData.SeekingTargetExpect, mChainInfo); //Console.WriteLine("重新索敌:" + mTarget.ID); } } this.mSyncInfo.ObjectID = base.ID; this.mLaunchDirection = this.Direction; float radius = mLaunchData.LaunchSpellRadius; if (mLaunchData.FromUnitBody && (Sender is InstanceUnit)) { radius = (Sender as InstanceUnit).Info.LaunchSpellRadius; } mStartPos = new Vector2(X, Y); switch (mInfo.MType) { case SpellTemplate.MotionType.SelectTarget: if (mTarget != null) { mStartPos.SetX(mTarget.X); mStartPos.SetY(mTarget.Y); } MathVector.movePolar(mStartPos, mLaunchDirection, radius); break; case SpellTemplate.MotionType.SelectLauncher: if (mLauncherUnit != null) { mStartPos.SetX(mLauncherUnit.X); mStartPos.SetY(mLauncherUnit.Y); } MathVector.movePolar(mStartPos, mLaunchDirection, radius); break; case SpellTemplate.MotionType.Immovability: MathVector.movePolar(mStartPos, mLaunchDirection, radius); break; case SpellTemplate.MotionType.Cannon: if (mTargetPos != null) { MathVector.moveTo(mStartPos, mTargetPos.X, mTargetPos.Y, radius); } else if (mTarget != null) { mTargetPos = new Vector2(mTarget.X, mTarget.Y); MathVector.moveTo(mStartPos, mTargetPos.X, mTargetPos.Y, radius); } break; case SpellTemplate.MotionType.AOE: MathVector.movePolar(mStartPos, mLaunchDirection, radius); break; case SpellTemplate.MotionType.AOE_Binding: break; case SpellTemplate.MotionType.AOE_BindingTarget: break; case SpellTemplate.MotionType.Straight: MathVector.movePolar(mStartPos, mLaunchDirection, radius); break; case SpellTemplate.MotionType.Binding: break; case SpellTemplate.MotionType.BindingTarget: break; case SpellTemplate.MotionType.Missile: if (mTarget != null) { MathVector.moveTo(mStartPos, mTarget.X, mTarget.Y, radius); } else { MathVector.movePolar(mStartPos, mLaunchDirection, radius); } break; case SpellTemplate.MotionType.MissileAttackRoute: if (mTarget != null) { MathVector.moveTo(mStartPos, mTarget.X, mTarget.Y, radius); } else { MathVector.movePolar(mStartPos, mLaunchDirection, radius); } //设置索敌间隔 if (this.Info.SeekingCooldownMS > XmdsConstConfig.GAME_UPDATE_INTERVAL_MS) { this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS); } else { log.Error("法术:" + this.Info.ID + ", 索敌间隔配置异常!"); } break; case SpellTemplate.MotionType.SeekerMissile: if (mTarget != null) { MathVector.moveTo(mStartPos, mTarget.X, mTarget.Y, radius); } else { MathVector.movePolar(mStartPos, mLaunchDirection, radius); if (mInfo.SeekingCooldownMS == 0) { mTarget = seekAttackable(mInfo.SeekingRange); } else { this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS); } } break; case SpellTemplate.MotionType.SeekerSelectTarget: if (mTarget == null) { if (mInfo.SeekingCooldownMS == 0) { mTarget = seekAttackable(mInfo.SeekingRange); if (mTarget != null) { mStartPos.SetX(mTarget.X); mStartPos.SetY(mTarget.Y); } } else { this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS); } } break; case SpellTemplate.MotionType.Chain: if (mSender != null) { mStartPos.SetX(mSender.X); mStartPos.SetY(mSender.Y); } break; case SpellTemplate.MotionType.Boomerang1: case SpellTemplate.MotionType.Boomerang2: MathVector.movePolar(mStartPos, mLaunchDirection, radius); break; case SpellTemplate.MotionType.CurveMissile: MathVector.movePolar(mStartPos, mLaunchDirection, radius); if (mInfo.SeekingCooldownMS > 0) { this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS); } break; case SpellTemplate.MotionType.Foxfire: MathVector.movePolar(mStartPos, mLaunchDirection, radius); mTarget = null; foxfireSeekWait = 0; break; case SpellTemplate.MotionType.StraightAndStop: MathVector.movePolar(mStartPos, mLaunchDirection, radius); break; } setPos(mStartPos.X, mStartPos.Y, this.LaunchHeightZ); } //public long mDisPosingTime = 0; //public long mDisPosingTime1 = 0; protected override void Disposing() { this.mSeekingCooldownTime = null; this.mKeyFrames = null; //mDisPosingTime = CommonLang.CUtils.localTimeMS; //mDisPosingTime1 = System.DateTime.Now.Ticks; if (this.mHittedUnits != null) { this.mHittedUnits.Clear(); this.mHittedUnits = null; } this.mHitIntervalTicker = null; this.mCreateSpellData = null; if (this.mCurveSpellList != null) { this.mCurveSpellList.Clear(); this.mCurveSpellList = null; } if (brothers != null) { for (int i = 0; i < brothers.Length; i++) { if (this == brothers[i]) { brothers[i] = null; break; } } } this.AttackRange = null; base.Disposing(); } protected override void onRemoved() { if (mInfo.StopBindingSkillOnRemoved) { if (this.Launcher != null && this.Launcher.CurrentState is InstanceUnit.StateSkill) { (this.Launcher.CurrentState as InstanceUnit.StateSkill).block(); this.Launcher.doSomething(); } } } override protected void onUpdate(bool slowRefresh) { if (IsPaused) { return; } if (Info.MaxMoveDistance == 0 || mMoveDistance < Info.MaxMoveDistance) { updateMotion(); if (mInfo.IsMoveable) { MoveHelper.UpdateSpeed(Parent.UpdateIntervalMS, ref mSpeed, mInfo.MSpeedAdd, mInfo.MSpeedAcc, mInfo.MSpeed_MIN, mInfo.MSpeed_MAX); MoveHelper.UpdateMoveDistance(Parent.UpdateIntervalMS, ref mMoveDistance, mSpeed); } } if (this.mKeyFrames != null) { updateKeyFrames(slowRefresh); } if (mInfo.MType == SpellTemplate.MotionType.Foxfire && mTarget != null) { } else if (mInfo.MType == SpellTemplate.MotionType.CurveMissile && mTarget != null) { } else if (mInfo.MType == SpellTemplate.MotionType.Boomerang1 || mInfo.MType == SpellTemplate.MotionType.Boomerang2) { } else if (mInfo.MType == SpellTemplate.MotionType.Binding)//绑定自身的spell才在自己死亡时移除 { if (!Sender.Enable || Sender.IsDead()) { Parent.RemoveObject(this); } else if ((Sender.AoiStatus == null && this.aoiFlag == 1) || (Sender.AoiStatus != null && this.aoiFlag == 0)) { //玩家AOI状态改变了,也要清一下 Parent.RemoveObject(this); } StateSkill skill = this.mLauncherUnit.CurrentState as StateSkill; if (skill != null && skill.Skill != null && skill.SkillData.ActionQueue.Count > 1) { //UnitActionData curSkillAction = skill.Skill.GetCurAction(); if (skill.CurrentActionIndex != this.actionIndex && (0 <= this.actionIndex && this.actionIndex < skill.SkillData.ActionQueue.Count) && skill.SkillData.ActionQueue[this.actionIndex].IsCancelBySkillNext) { Parent.RemoveObject(this); } } } if (PassTimeMS >= this.mSpellTotalTime) { Parent.RemoveObject(this);//Parent.RemoveObjectByID(ID); } } /// <summary> /// 更新移动行为 /// </summary> private void updateMotion() { this.mPrvePos.SetX(X); this.mPrvePos.SetY(Y); switch (mInfo.MType) { case SpellTemplate.MotionType.Immovability: case SpellTemplate.MotionType.SelectTarget: case SpellTemplate.MotionType.SelectLauncher: break; case SpellTemplate.MotionType.Cannon: if (mTargetPos != null) { if (flyToTarget(mTargetPos.X, mTargetPos.Y, mSpeed, Parent.UpdateIntervalMS)) { this.Finish = true; } } else if (PassTimeMS + Parent.UpdateIntervalMS >= this.mSpellTotalTime)//没有目标,结束时触发下 { if (Parent.UpdateIntervalMS > 0) { this.Finish = true; } } break; case SpellTemplate.MotionType.Straight: flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS); break; case SpellTemplate.MotionType.AOE: updateAOE(); break; case SpellTemplate.MotionType.AOE_Binding: updateAOE(); if (mSender != null && mSender.Enable) { updateBinding(mSender); } else { Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.AOE_BindingTarget: updateAOE(); if (mTarget != null && mTarget.IsActive) { updateBinding(mTarget); } else { Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.Binding: if (mSender != null && mSender.Enable) { updateBinding(mSender); } else { Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.BindingTarget: if (mTarget != null && mTarget.IsActive) { updateBinding(mTarget); } else { Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.Missile: case SpellTemplate.MotionType.MissileAttackRoute: if (mTarget != null) { //if (mInfo.RotateSpeedSEC == 0) { faceTo(mTarget.X, mTarget.Y); } flyToTarget(mTarget.X, mTarget.Y, mSpeed, Parent.UpdateIntervalMS); } else { flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS); } break; case SpellTemplate.MotionType.SeekerMissile: if (mTarget != null) { if (mInfo.SeekingTurningAngleSEC != 0) { flyToTargetTunning(mTarget.X, mTarget.Y, mSpeed, mInfo.SeekingTurningAngleSEC, Parent.UpdateIntervalMS); } else { faceTo(mTarget.X, mTarget.Y); flyToTarget(mTarget.X, mTarget.Y, mSpeed, Parent.UpdateIntervalMS); } } else { flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS); if (mSeekingCooldownTime == null || mSeekingCooldownTime.Update(Parent.UpdateIntervalMS)) { mTarget = seekAttackable(mInfo.SeekingRange); if (mTarget != null) { Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, mTarget.ID, X, Y)); } } } break; case SpellTemplate.MotionType.SeekerSelectTarget: if (mTarget == null) { if (mSeekingCooldownTime == null || mSeekingCooldownTime.Update(Parent.UpdateIntervalMS)) { mTarget = seekAttackable(mInfo.SeekingRange); if (mTarget != null) { this.setPos(mTarget.X, mTarget.Y); Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, mTarget.ID, X, Y)); } } } break; case SpellTemplate.MotionType.Chain: if (mSender != null && mSender.Enable && mTarget != null && mTarget.IsActive) { setPos(mSender.X, mSender.Y); faceTo(mTarget.X, mTarget.Y); updateBinding(mTarget); } else { Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.Boomerang1: if (PassTimeMS < mInfo.BoomerangFlyTime) { flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS); } else if (PassTimeMS > mInfo.BoomerangFlyTime + mInfo.BoomerangHangtime) { if (flyToTarget(mLauncherUnit.X, mLauncherUnit.Y, mSpeed * 1.4f, Parent.UpdateIntervalMS)) { this.Finish = true; } } break; case SpellTemplate.MotionType.Boomerang2: if (PassTimeMS < mInfo.BoomerangFlyTime) { flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS); } else if (PassTimeMS > mInfo.BoomerangFlyTime + mInfo.BoomerangHangtime) { if (flyToTarget(mStartPos.X, mStartPos.Y, mSpeed * 1.4f, Parent.UpdateIntervalMS)) { this.Finish = true; } } break; case SpellTemplate.MotionType.CurveMissile: //IPosBase targetPos = mTargetPos; IPosBase targetPos = (mTarget != null) ? (IPosBase)mTarget : (IPosBase)mTargetPos; if (targetPos == null) { flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS); } else if (mInfo.SeekingTurningAngleSEC != 0) { flyToTargetTunning(targetPos.X, targetPos.Y, mSpeed, mInfo.SeekingTurningAngleSEC, Parent.UpdateIntervalMS); } else { faceTo(targetPos.X, targetPos.Y); flyToTarget(targetPos.X, targetPos.Y, mSpeed, Parent.UpdateIntervalMS); } if (mTarget == null && (mSeekingCooldownTime == null || mSeekingCooldownTime.Update(Parent.UpdateIntervalMS))) { mTarget = seekAttackable(mInfo.SeekingRange); if (mTarget != null) { Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, mTarget.ID, X, Y)); } } this.checkAndLaunchCurveSpell(); break; case SpellTemplate.MotionType.Foxfire: if (mTarget == null) { foxfireRoundAngle = mLaunchDirection + PassTimeMS * 0.001f * 3; var x = mLauncherUnit.X + (float)(Math.Cos(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius); var y = mLauncherUnit.Y + (float)(Math.Sin(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius); flyToTarget(x, y, mSpeed, Parent.UpdateIntervalMS); if (mInfo.RotateSpeedSEC == 0) { faceTo(foxfireRoundAngle + (float)Math.PI * 0.25f); } if (foxfireSeekWait >= this.Info.SeekingCooldownMS && PassTimeMS < this.mSpellTotalTime - 500) { var tar = seekAttackable(mInfo.SeekingRange); if (tar != null && tar != mLauncherUnit) { changeBrotherSeekWait(-250); Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, tar.ID, X, Y)); mTarget = tar; } } foxfireSeekWait += Parent.UpdateIntervalMS; } else { if (mInfo.SeekingTurningAngleSEC != 0) { flyToTargetTunning(mTarget.X, mTarget.Y, mSpeed, mInfo.SeekingTurningAngleSEC, Parent.UpdateIntervalMS); } else { faceTo(mTarget.X, mTarget.Y); flyToTarget(mTarget.X, mTarget.Y, mSpeed, Parent.UpdateIntervalMS); } } break; case SpellTemplate.MotionType.StraightAndStop: if (PassTimeMS < mInfo.BoomerangFlyTime) { flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS); } //else if (PassTimeMS > mInfo.BoomerangFlyTime + mInfo.BoomerangHangtime) //{ // if (flyToTarget(mLauncherUnit.X, mLauncherUnit.Y, mSpeed * 1.8f, Parent.UpdateIntervalMS)) // { // this.Finish = true; // } //} break; } if (mInfo.BodyShape == SpellTemplate.Shape.LineToTarget) { if (mTarget != null) { this.faceTo(mTarget.X, mTarget.Y); } } else if (mInfo.BodyShape == SpellTemplate.Shape.LineToStart) { this.faceTo(mStartPos.X, mStartPos.Y); } else if (mInfo.RotateSpeedSEC != 0 && mInfo.MType != SpellTemplate.MotionType.SeekerMissile) { this.turn(MoveHelper.GetDistance(Parent.UpdateIntervalMS, mInfo.RotateSpeedSEC)); } } /** 检测释放后续飞弹 */ private void checkAndLaunchCurveSpell(bool force = false) { //////创建后续个飞弹 if ((force || PassTimeMS >= XmdsConstConfig.GAME_UPDATE_INTERVAL_MS) && mCurveSpellIndex > 0 && this.Launcher.IsActive) { int nextSpellIndex = Math.Max(0, mCurveSpellIndex - 1); int flag = (this.mCreateSpellData.mMaxSpellCount - mCurveSpellIndex) % 2 == 0 ? 2 : -2; //float angleOffset = (this.Target == null ? this.LaunchData.StartAngle / 3 : this.LaunchData.StartAngle); float angleOffset = JSGModule.GetCurveStartAngleOffect(mLauncherUnit, this.mTarget, this.LaunchData.StartAngle, this.Info.SeekingRange); float startDirection = this.mLaunchDirection + angleOffset * flag; float launchX = this.Launcher.X - (float)(1.0f * Math.Cos(this.Launcher.Direction)); float launchY = this.Launcher.Y - (float)(1.0f * Math.Sin(this.Launcher.Direction)); LaunchSpell launchSpell = this.LaunchData; if (mCurveSpellList != null && mCurveSpellList.Count > mCurveSpellIndex) { launchSpell = mCurveSpellList[mCurveSpellIndex]; } InstanceSpell addSpell = this.Parent.AddSpell(this.FromSkillType, this.Info, launchSpell, this.Sender, this.mLauncherUnit, (this.mTarget == null ? 0 : this.mTarget.ID), JSGModule.GetCurveDefaultTargetPos(mLauncherUnit, this.Info.SeekingRange), launchX, launchY, startDirection, null, -1, 0, null, nextSpellIndex); addSpell.mCurveSpellList = this.mCurveSpellList; mCurveSpellIndex = 0; } } public static double AngleBetween(Vector2 vector1, Vector2 vector2) { double sin = vector1.X * vector2.Y - vector2.X * vector1.Y; double cos = vector1.X * vector2.X + vector1.Y * vector2.Y; return Math.Atan2(sin, cos); } private void updateAOE() { switch (Info.BodyShape) { case SpellTemplate.Shape.LineToTarget: case SpellTemplate.Shape.LineToStart: case SpellTemplate.Shape.Strip: case SpellTemplate.Shape.StripRay: case SpellTemplate.Shape.StripRayTouchEnd: case SpellTemplate.Shape.RectStrip: case SpellTemplate.Shape.RectStripRay: updateAoeMotion(Info.Distance, ref mDistance); break; default: updateAoeMotion(Info.BodySize, ref mBaseSize); break; } } private void updateAoeMotion(float base_value, ref float value) { switch (Info.AOEMType) { case SpellTemplate.AoeMotionType.Sine: value = (float)Math.Sin(CMath.PI_F * PassTimeMS / (float)this.mSpellTotalTime) * base_value; break; case SpellTemplate.AoeMotionType.Linear: default: value += MoveHelper.GetDistance(Parent.UpdateIntervalMS, mSpeed); break; } } private void updateBinding(InstanceZoneObject target) { if ((target is InstanceUnit)) { var target_unit = target as InstanceUnit; if (mInfo.RemoveOnBindingUncontrollable) { // 目标不可操控,停止法术 // if (target_unit.IsControllable == false) { Parent.RemoveObject(this); return; } } if (mInfo.RemoveOnBindingSkillOver) { // 目标非技能状态,停止法术 // if (target_unit.CurrentState is InstanceUnit.StateSkill) { if (mBindingSkill == null) { mBindingSkill = target_unit.CurrentState as InstanceUnit.StateSkill; } else if (mBindingSkill != target_unit.CurrentState) { Parent.RemoveObject(this); return; } } else { Parent.RemoveObject(this); return; } } } if (mInfo.IsBinding) { if (mInfo.IsBindingDirection) { this.faceTo(target.Direction); } if (mInfo.IsBindingOrbit) { float dadd = mInfo.OrbitDistance; float ox = (float)Math.Cos(Direction) * dadd; float oy = (float)Math.Sin(Direction) * dadd; this.setPos(target.X + ox, target.Y + oy); } else { float xChanage, yChange; this.LaunchData.GetXModify(this.Launcher, out xChanage, out yChange); this.setPos(target.X + xChanage, target.Y + yChange); } } } private void affectToSingle(InstanceUnit target, SpellTemplate.KeyFrame kf) { if (kf == null) return; if (kf.Effect != null) { Parent.queueObjectEvent(this, new UnitEffectEvent(this.ID, kf.Effect)); } // 法术造成伤害 if (kf.Attack != null) { Parent.unitAttackSingle(mLauncherUnit, new AttackSource(this, kf.Attack), target, mInfo.ExpectTarget); } // 法术产生法术 if (kf.Spell != null) { Parent.spellLaunchSpell(this.FromSkillType, this, kf.Spell, this.X, this.Y, target.ID); //Parent.spellLaunchSpell(this, kf.Spell, mLauncherUnit.X + 5, mLauncherUnit.Y + 5, target.ID); } // 召唤 if (kf.Summon != null) { Parent.spellSummonUnit(this, kf.Summon); } } private void affectToMulti(List<InstanceUnit> list, SpellTemplate.KeyFrame kf, bool effect) { if (kf == null) return; if (effect && kf.Effect != null) { Parent.queueObjectEvent(this, new UnitEffectEvent(this.ID, kf.Effect)); } uint targetUnitID = 0; if (list.Count > 0) { targetUnitID = list[0].ID; // 法术造成伤害 if (kf.Attack != null) { Parent.unitAttackDirect(mLauncherUnit, new AttackSource(this, kf.Attack), list); } } // 法术产生法术 if (kf.Spell != null) { Parent.spellLaunchSpell(this.FromSkillType, this, kf.Spell, this.X, this.Y, targetUnitID); //System.Console.WriteLine("------法术产生法术: " + kf.Spell.SpellID + ", " + this.X + ", " + this.Y); } // 召唤 if (kf.Summon != null) { Parent.spellSummonUnit(this, kf.Summon); } } #region __检测范围内目标__ // 跟踪导弹查找附近单位 // public InstanceUnit seekAttackable(float range) { return Parent.SeekSpellAttackable(this.Launcher, this.Info, this.AoiStatus, X, Y, range, mInfo.ExpectTarget, mInfo.SeekingExpectTarget, mChainInfo); } // 范围伤害,碰撞检测 // private void getShapeAttackable(List<InstanceUnit> ret, AttackReason reason, bool slowRefresh) { AttackRange.Shape = (AttackShape)mInfo.BodyShape; AttackRange.BodySize = this.mBaseSize; AttackRange.Direction = this.Direction; AttackRange.Distance = this.mDistance; AttackRange.ExpectTarget = mInfo.ExpectTarget; AttackRange.FanAngle = this.mInfo.FanAngle; AttackRange.StripWide = this.mInfo.RectWide; float prv_x = this.X; float prv_y = this.Y; if (mInfo.MType == SpellTemplate.MotionType.Straight || mInfo.MType == SpellTemplate.MotionType.StraightAndStop) { prv_x = mPrvePos.X; prv_y = mPrvePos.Y; } AttackRange.GetShapeAttackable(ret, reason, this.Info, X, Y, prv_x, prv_y, mInfo.MaxAffectUnit, AoiStatus); } #endregion float foxfireRoundAngle = 0f; int foxfireSeekWait = 0; public InstanceSpell[] brothers = null; void changeBrotherSeekWait(int timeValue) { if (brothers == null) { return; } foreach (var b in brothers) { if(b != null) { b.foxfireSeekWait += timeValue; } } } /// <summary> /// 更新范围检测以及关键帧 /// </summary> private void updateKeyFrames(bool slowRefresh) { switch (Info.MType) { case SpellTemplate.MotionType.Missile: case SpellTemplate.MotionType.SeekerMissile: //【战斗】Missile类技能,在目标消失后失效// if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize, this.X, this.Y)) { if (mTarget.IsActive) { affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame); } Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.CurveMissile: //【战斗】Missile类技能,在目标消失后失效// if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize + this.BodyHitSize, this.X, this.Y)) { if (mTarget.IsActive) { affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame); } this.checkAndLaunchCurveSpell(true); Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.MissileAttackRoute: if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize, this.X, this.Y)) { if (mTarget.IsActive) { affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame); } Parent.RemoveObject(this); } else if (mSeekingCooldownTime.Update(Parent.UpdateIntervalMS)) { using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease()) { getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh); if (enemy_list.Count > 0) { InstanceUnit target = enemy_list[0]; if (target.IsActive) { affectToSingle(target, mInfo.HitOnExplosionKeyFrame); } mSeekingCooldownTime.Reset(); } } } break; case SpellTemplate.MotionType.Cannon: if (Finish) { SpellTemplate.KeyFrame kf = mInfo.HitOnExplosionKeyFrame; if (kf != null) { using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease()) { getShapeAttackable(list, AttackReason.Attack, slowRefresh); affectToMulti(list, kf, true); } } Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.Chain: if (mTarget != null) { if (Collider.Object_Pos_IncludeInRound(mTarget, this.X, this.Y, this.mDistance)) { updateKeyFrameSingleTarget(mTarget, true); } else { Parent.RemoveObject(this); } } break; case SpellTemplate.MotionType.Boomerang1: case SpellTemplate.MotionType.Boomerang2: if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DragPath && Info.SpecialEffectParam != 0f) { updateDragPathKeyFramesRanged(slowRefresh); } else if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DarkHole && Info.SpecialEffectParam != 0f) { updateDarkHoleKeyFramesRanged(slowRefresh); } else { updateKeyFramesRanged(slowRefresh); } if (Finish) { Parent.RemoveObject(this); } break; case SpellTemplate.MotionType.Foxfire: if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize, this.X, this.Y)) { if (mTarget.IsActive) { affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame); } Parent.RemoveObject(this); } break; default: if (Info.BodyShape == SpellTemplate.Shape.LineToTarget || Info.BodyShape == SpellTemplate.Shape.LineToStart) { if (mTarget != null && Collider.Object_Pos_IncludeInRound(mTarget, this.X, this.Y, this.mDistance)) { updateKeyFrameSingleTarget(mTarget, true); } else { updateKeyFrameSingleTarget(mTarget, false); } } else { if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DragPath && Info.SpecialEffectParam != 0f) { updateDragPathKeyFramesRanged(slowRefresh); } else if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DarkHole && Info.SpecialEffectParam != 0f) { updateDarkHoleKeyFramesRanged(slowRefresh); } else { updateKeyFramesRanged(slowRefresh); } } break; } } private void updateKeyFrameSingleTarget(InstanceUnit enemy, bool affect) { using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease()) { int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs); bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS); if (affect) { if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0) { if (kfs_count > 0) { for (int i = 0; i < kfs.Count; i++) { affectToSingle(enemy, kfs[i]); } } if (mInfo.HitOnExplosion) { // 击中后爆炸 affectToSingle(enemy, mInfo.HitOnExplosionKeyFrame); Parent.RemoveObject(this); } else if (mInfo.HitIntervalKeyFrame != null) { if (mInfo.HitIntervalMS == 0) { // 只在接触后第一次产生效果 if (!mHittedUnits.ContainsKey(enemy.ID)) { affectToSingle(enemy, mInfo.HitIntervalKeyFrame); mHittedUnits.Add(enemy.ID, enemy); } } else if (is_interval_test) { // 间隔产生效果 affectToSingle(enemy, mInfo.HitIntervalKeyFrame); } } } } } } private void updateKeyFramesRanged(bool slowRefresh) { using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease()) { //if(mKeyFrames == null) //{ // long ltime1 = CommonLang.CUtils.localTimeMS - mDisPosingTime; // long ltime2 = System.DateTime.Now.Ticks - mDisPosingTime1; //} int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs); bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS); //此处修正 加入触发次数上限检测 if (is_interval_test && mInfo.HitIntervalMS > 0 && mHitIntervalTicker.TotalTickCount > (this.mSpellTotalTime / mInfo.HitIntervalMS)) { is_interval_test = false; } if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0) { this.mHitTotalTimes++; using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease()) { getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh); if (kfs_count > 0) { // 单Frame情况下,如果是Attack。进行唯一敌人判定(X型拼接法术, 需要伤害只生效一次) //if (kfs_count == 1 && mInfo.HitIntervalMS == 0 && this.mInfo.KeyFrames.Count == 1 && kfs[0].Attack != null && this.mInfo.HitIntervalKeyFrame == null) if(this.LaunchData.PType == LaunchSpell.PosType.POS_TYPE_X) { if(this.refreshHitUnit(enemy_list)) { for (int i = 0; i < kfs.Count; i++) { affectToMulti(enemy_list, kfs[i], true); } } } else { for (int i = 0; i < kfs.Count; i++) { affectToMulti(enemy_list, kfs[i], true); } } } if (mInfo.HitOnExplosion) { if (enemy_list.Count > 0) { // 击中后爆炸 affectToMulti(enemy_list, mInfo.HitOnExplosionKeyFrame, true); Parent.RemoveObject(this); } } else if(mInfo.HitIntervalKeyFrame != null) { if (mInfo.HitIntervalMS == 0) { // 只在接触后第一次产生效果 if (enemy_list.Count > 0 && this.refreshHitUnit(enemy_list)) { affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, true); } } else if (is_interval_test) { // 间隔产生效果 affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, false); } } } } } } private void updateDarkHoleKeyFramesRanged(bool slowRefresh) { using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease()) { using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease()) { getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh); for (int i = enemy_list.Count - 1; i >= 0; --i) { if(enemy_list[i].IsIgnoreControl) { continue; } float angle, distance, offset, dx, dy; if (enemy_list[i].Weight < XmdsConstConfig.DEFAULT_WEIGHT) { offset = MoveHelper.GetDistance(Parent.UpdateIntervalMS, Info.SpecialEffectParam); dx = this.X - enemy_list[i].X; dy = this.Y - enemy_list[i].Y; distance = dx * dx + dy * dy; if (offset * offset < distance) { angle = MathVector.getDegree(dx, dy); enemy_list[i].moveLinearMap(offset, angle); } else { enemy_list[i].moveLinearMap2(dx, dy); } enemy_list[i].SetStunTimeMS(100); } } int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs); bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS); #region <<关键帧逻辑>> if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0) { //getShapeAttackable(enemy_list, AttackReason.Attack); if (kfs_count > 0) { for (int i = 0; i < kfs.Count; i++) { affectToMulti(enemy_list, kfs[i], false); } } if (mInfo.HitOnExplosion) { if (enemy_list.Count > 0) { // 击中后爆炸 affectToMulti(enemy_list, mInfo.HitOnExplosionKeyFrame, true); Parent.RemoveObject(this); } } else if(mInfo.HitIntervalKeyFrame != null) { if (mInfo.HitIntervalMS == 0) { // 只在接触后第一次产生效果 if (enemy_list.Count > 0 && this.refreshHitUnit(enemy_list)) { affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, true); } } else if (is_interval_test) { // 间隔产生效果 affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, false); } } } #endregion } } } public override String GetTemplateData() { return "Spell:" + this.Info.ID; } private bool refreshHitUnit(CommonLang.ListObjectPool<InstanceUnit>.AutoReleaseList<InstanceUnit> enemy_list) { if(enemy_list.Count <= 0) { return true; } foreach (InstanceUnit u in mHittedUnits.Values) { enemy_list.Remove(u); } if (enemy_list.Count > 0) { for (int i = enemy_list.Count - 1; i >= 0; --i) { InstanceUnit u = enemy_list[i]; mHittedUnits.Add(u.ID, u); } return true; } return false; } public const float Pi = (float)Math.PI; public const float PiOver2 = (float)(Math.PI / 2.0); public const float PiOver36 = (float)(Math.PI / 36.0); public const float TwoPi = (float)(Math.PI * 2.0); private void updateDragPathKeyFramesRanged(bool slowRefresh) { using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease()) { using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease()) { getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh); float _a, _b, _angle, offset, dx, dy , dis; for (int i = enemy_list.Count - 1; i >= 0; --i) { if (enemy_list[i].IsIgnoreControl) { continue; } if (enemy_list[i].Weight < XmdsConstConfig.DEFAULT_WEIGHT) { dx = enemy_list[i].X - this.X; dy = enemy_list[i].Y - this.Y; _a = MathVector.getDegree(dx, dy); _b = this.Direction - _a; if (_b < -Pi) { _b += TwoPi; } else if(_b > Pi) { _b -= TwoPi; } if (Math.Abs(_b) < PiOver36) { continue; } if(_b > 0) { if(_b > PiOver2) { continue; } _angle = this.Direction + PiOver2; } else { if (_b < -PiOver2) { continue; } _angle = this.Direction - PiOver2; } dis = Math.Abs((float)(dy * Math.Sin(_angle) / Math.Sin(_a))); offset = MoveHelper.GetDistance(Parent.UpdateIntervalMS, Info.SpecialEffectParam); if(offset > dis) { offset = dis; } enemy_list[i].moveLinearMap(offset, _angle); enemy_list[i].SetStunTimeMS(100); } } int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs); bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS); #region <<关键帧逻辑>> if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0) { //getShapeAttackable(enemy_list, AttackReason.Attack); if (kfs_count > 0) { for (int i = 0; i < kfs.Count; i++) { affectToMulti(enemy_list, kfs[i], false); } } if (mInfo.HitOnExplosion) { if (enemy_list.Count > 0) { // 击中后爆炸 affectToMulti(enemy_list, mInfo.HitOnExplosionKeyFrame, true); Parent.RemoveObject(this); } } else if (mInfo.HitIntervalKeyFrame != null) { if (mInfo.HitIntervalMS == 0) { // 只在接触后第一次产生效果 if (enemy_list.Count > 0 && this.refreshHitUnit(enemy_list)) { affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, true); } } else if (is_interval_test) { // 间隔产生效果 affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, false); } } } #endregion } } } } }