//a method that launches the attack object towards the target public void Launch(EffectObjPool effectPool, AttackEntity source) { AttackObject newAttackObject = effectPool.SpawnEffectObj(attackObject, launchPosition.position, Quaternion.identity).GetComponent <AttackObject>(); Vector3 targetPosition = source.GetTargetPosition(); if (GameManager.MultiplayerGame == false) //if this is a singleplayer game, we can play with accuracy: { targetPosition += new Vector3(Random.Range(-accuracyModifier.x, accuracyModifier.x), Random.Range(-accuracyModifier.y, accuracyModifier.y), Random.Range(-accuracyModifier.z, accuracyModifier.z)); } newAttackObject.Enable(source, targetPosition, (createInDelay == true) ? delayTime : 0.0f, damageInDelay, delayParentObject, source.CanEngageFriendly()); }
void Update() { //Unit only: if (Attacker == Attackers.Unit) { //the attack animation timer: if (AttackAnimTimer > 0) { AttackAnimTimer -= Time.deltaTime; } if (AttackAnimTimer < 0) { AttackAnimTimer = 0; //stop showing the attacking animation when the timer is done. if (UnitMgr.AnimMgr) { if (UnitMgr.AnimMgr.GetBool("IsAttacking") == true) { UnitMgr.AnimMgr.SetBool("IsAttacking", false); } } } } //Attack timer: if (AttackTimer > 0) { AttackTimer -= Time.deltaTime; } if (CanAttack() == true) //If the attacker is still not dead/destroyed { if (GameMgr.PeaceTime <= 0) //if we're not in the peace time { if (AttackTarget == null) { //If the target is not set yet if (WeaponObj != null) //if there's a weapon object { if (SmoothRotation == false) { //if the rotation is automatically changed WeaponObj.transform.localRotation = WeaponIdleRotation; } else { //smooth rotation here: WeaponObj.transform.localRotation = Quaternion.Slerp(WeaponObj.transform.localRotation, WeaponIdleRotation, Time.deltaTime * RotationDamping); } } if (GameManager.MultiplayerGame == false || (GameManager.MultiplayerGame == true && GameManager.PlayerFactionID == AttackerFactionID())) { //if this is an offline game or online but this is the local player if (AttackInRange == true) //if the attacker can attack in range ~and it's also in idle state~ { //if the faction is a NPC or a local player and having a target is required. if (GameManager.PlayerFactionID != AttackerFactionID() || (GameManager.PlayerFactionID == AttackerFactionID() && RequireAttackTarget == true)) { //Search timer: if (SearchTimer > 0) { SearchTimer -= Time.deltaTime; } else { //Search if there are enemy units in range: bool Found = false; float Size = SearchEnemyRange; Vector3 SearchFrom = transform.position; Collider[] ObjsInRange = Physics.OverlapSphere(SearchFrom, Size); // ADD LAYERMASK System.Array.Sort(ObjsInRange, DistanceSort); int i = 0; //counter while (i < ObjsInRange.Length && Found == false) { Unit UnitInRange = ObjsInRange[i].gameObject.GetComponent <Unit>(); if (UnitInRange) { //If it's a unit object if (UnitInRange.enabled == true) { //if the unit comp is enabled //If this unit and the target have different teams and make sure it's not dead. if (UnitInRange.FactionID != AttackerFactionID() && UnitInRange.Dead == false) { //if the unit is visible: if (UnitInRange.IsInvisible == false) { if (AttackTarget == null) { if (AttackManager.Instance.CanAttackTarget(this, ObjsInRange[i].gameObject, AttackCategoriesList)) { //if the unit can attack the target. //Set this unit as the target SetAttackTarget(ObjsInRange[i].gameObject); Found = true; } } } } } } i++; } if (Found == false) { SearchTimer = SearchReload; //No enemy units found? search again. } else { //if the attacker is a unit: if (Attacker == Attackers.Unit) { //Follow the target: UnitMgr.CheckUnitPath(Vector3.zero, AttackTarget, AttackRange + AttackTarget.GetComponent <Unit>().NavAgent.radius + UnitMgr.NavAgent.radius, 0, true); } } } } } } } else { //if the target went invisible: //Checking whether the target is dead or not (if it's a unit or a building. bool Dead = false; if (AttackTarget.GetComponent <Unit> ()) { //if the target went invisible: if (AttackTarget.GetComponent <Unit> ().IsInvisible == true) { //stop attacking it: CancelAttack(); return; } else if (AttackTarget.GetComponent <Unit> ().FactionID == AttackerFactionID()) { CancelAttack(); return; } Dead = AttackTarget.GetComponent <Unit> ().Dead; } else if (AttackTarget.GetComponent <Building> ()) { if (AttackTarget.GetComponent <Building> ().FactionID == AttackerFactionID()) { CancelAttack(); return; } //Dead = !(Target.GetComponent<Building> ().Health > 0); } if (Dead == true && AttackTimer <= 0) { //if the target unit is dead, cancel everything CancelAttack(); } else { //If the current unit has a target, if (Vector3.Distance(this.transform.position, AttackTarget.transform.position) > FollowRange && AttackRangeFromCenter == false && AttackTarget.GetComponent <Unit> () && WasInTargetRange == true) //This means that the target has left the follow range of the unit. { CancelAttack(); //This unit doesn't have a target anymore. } else { if (Attacker == Attackers.Unit) //if the attacker is a unit { //if this is the local player in a MP game or simply an offline game: if (GameManager.MultiplayerGame == false || (GameManager.MultiplayerGame == true && GameManager.PlayerFactionID == AttackerFactionID())) { //if the attack target went away: if (Vector3.Distance(UnitMgr.NavAgent.destination, AttackTarget.transform.position) > ChangeMvtDistance && UnitMgr.Moving == true) { UnitMgr.CheckUnitPath(Vector3.zero, AttackTarget, UnitMgr.NavAgent.stoppingDistance, 0, true); } //if destination is reached but the unit is still far away from the target. if (Vector3.Distance(UnitMgr.NavAgent.destination, AttackTarget.transform.position) > UnitMgr.NavAgent.stoppingDistance && UnitMgr.DestinationReached == true) { //THE PROBLEM IS HERE: When unit reaches the destination but max distance is probably more UnitMgr.DestinationReached = false; } if (UnitMgr.DestinationReached == false && UnitMgr.Moving == false) { //If the unit didn't reach its target and it looks like it's not moving: //Follow the target: UnitMgr.CheckUnitPath(Vector3.zero, AttackTarget, UnitMgr.NavAgent.stoppingDistance, 0, true); } } if (!MoveOnAttack && Vector3.Distance(transform.position, AttackTarget.transform.position) <= AttackRange) { UnitMgr.DestinationReached = true; } //if the attacker is in the correct range of his target if (UnitMgr.DestinationReached == true) { if (MoveOnAttack == false && UnitMgr.Moving == true) //if move on attack is disabled and the unit is still moving { return; //stop } } else { return; //if the unit didn't reach the target then don't proceed } } //reaching this stage means that the attacker is in range of the target and it's all good to go: WasInTargetRange = true; //Make the attack object look at the target: if (WeaponObj != null) { Vector3 LookAt = AttackTarget.transform.position - WeaponObj.transform.position; //which axis should not be rotated? if (FreezeRotX == true) { LookAt.x = 0.0f; } if (FreezeRotY == true) { LookAt.y = 0.0f; } if (FreezeRotZ == true) { LookAt.z = 0.0f; } Quaternion TargetRotation = Quaternion.LookRotation(LookAt); if (SmoothRotation == false) { //if the rotation is automatically changed WeaponObj.transform.rotation = TargetRotation; } else { //smooth rotation here: WeaponObj.transform.rotation = Quaternion.Slerp(WeaponObj.transform.rotation, TargetRotation, Time.deltaTime * RotationDamping); } } //Delay timer here: if (DelayTimer > 0) { DelayTimer -= Time.deltaTime; } if (Attacker == Attackers.Unit) { //Playing animation: if (UnitMgr.AnimMgr && (PlayAnimInDelay == true || (PlayAnimInDelay == false && DelayTimer <= 0.0f && AttackTriggered == true))) { if (UnitMgr.AnimMgr.GetBool("IsAttacking") == false) { UnitMgr.AnimMgr.SetBool("IsAttacking", true); } } } //If the attack delay is over, launch the attack if (DelayTimer <= 0.0f && AttackTriggered == true) { if (AttackTimer <= 0) { //if the attack timer is ready: //Custom event: if (GameMgr.Events) { GameMgr.Events.OnAttackPerformed(this, AttackTarget, AttackID); } //Is this a direct attack (no use of attack objects)? if (DirectAttack == true) { if (AreaDamage == true) { //launch area damage and provide all arguments: AttackManager.Instance.LaunchAreaDamage(AttackTarget.transform.position, AttackRanges, AttackerFactionID(), AttackEffect, AttackEffectTime, AttackCategoriesList, DoT); } else if (DoT.Enabled == true) { Unit TargetUnit = AttackTarget.GetComponent <Unit>(); //only for units currently? if (TargetUnit) { //DoT settings: TargetUnit.DoT = DoT; TargetUnit.DoT.Damage = AttackManager.GetDamage(AttackTarget, UnitDamage); } } else { //if this is no areal damage and no DoT //deal damage to unit/building: if (AttackTarget.GetComponent <Unit>()) { AttackTarget.GetComponent <Unit>().AddHealth(-AttackManager.GetDamage(AttackTarget, UnitDamage), this.gameObject); //Spawning the damage effect object: AttackManager.Instance.SpawnEffectObj(AttackTarget.GetComponent <Unit>().DamageEffect, AttackTarget, 0.0f, true); //spawn attack effect object: units only currently AttackManager.Instance.SpawnEffectObj(AttackEffect, AttackTarget, AttackEffectTime, false); } else if (AttackTarget.GetComponent <Building>()) { AttackTarget.GetComponent <Building>().AddHealth(-AttackManager.GetDamage(AttackTarget, UnitDamage), this.gameObject); //Spawning the damage effect object: AttackManager.Instance.SpawnEffectObj(AttackTarget.GetComponent <Building>().DamageEffect, AttackTarget, 0.0f, true); } } //Play the attack audio: AudioManager.PlayAudio(gameObject, AttackSound, false); if (Attacker == Attackers.Unit) { UnitMgr.SetAnimState(UnitAnimState.Attacking); } //reload the timers AttackTimer = AttackReload; AttackAnimTimer = AttackAnimTime; } else { //If the unit can launch attack objs towards the target unit if (AttackStep < AttackSources.Length && AttackSources.Length > 0) { //if we haven't already launched attacks from all sources if (AttackStepTimer > 0) { AttackStepTimer -= Time.deltaTime; } if (AttackStepTimer <= 0) { AttackTimer = AttackReload; AttackAnimTimer = AttackAnimTime; AttackObject AttackObj = ObjPool.GetEffectObj(EffectObjPool.EffectObjTypes.AttackObj, AttackSources[AttackStep].AttackObj).GetComponent <AttackObject>(); AttackObj.transform.position = AttackSources[AttackStep].AttackObjSource.transform.position; //Set the attack object's position: AttackObj.gameObject.SetActive(true); //Activate the attack object AttackObj.DefaultUnitDamage = UnitDamage; AttackObj.DefaultBuildingDamage = BuildingDamage; AttackObj.CustomDamage = CustomDamage; Vector3 TargetPos = AttackTarget.transform.position; if (AttackTarget.GetComponent <Unit>()) { AttackObj.TargetFactionID = AttackTarget.GetComponent <Unit>().FactionID; //TargetPos = AttackTarget.GetComponent<Unit>().PlayerSelection.transform.position; } else if (AttackTarget.GetComponent <Building>()) { AttackObj.TargetFactionID = AttackTarget.GetComponent <Building>().FactionID; //TargetPos = AttackTarget.GetComponent<Building>().PlayerSelection.transform.position; } if (AttackSources[AttackStep].FollowTarget) { AttackObj.FollowTarget = AttackSources[AttackStep].FollowTarget; AttackObj.Target = AttackTarget.transform; } AttackObj.DamageOnce = AttackSources[AttackStep].DamageOnce; AttackObj.DestroyOnDamage = AttackSources[AttackStep].DestroyAttackObjOnDamage; AttackObj.Source = gameObject; AttackObj.SourceFactionID = AttackerFactionID(); if (AreaDamage == false) { AttackObj.DidDamage = false; AttackObj.DoDamage = !DirectAttack; AttackObj.AreaDamage = false; } else { AttackObj.DamageOnce = true; AttackObj.DidDamage = false; AttackObj.AreaDamage = true; AttackObj.AttackRanges = AttackRanges; AttackObj.AttackCategoriesList = AttackCategoriesList; } //Damage over time: AttackObj.DoT = DoT; //Attack object movement: AttackObj.MvtVector = (TargetPos - AttackSources[AttackStep].AttackObjSource.transform.position) / Vector3.Distance(AttackTarget.transform.position, AttackSources[AttackStep].AttackObjSource.transform.position); AttackObj.Speed = AttackSources[AttackStep].AttackObjSpeed; //Set the attack obj's rotation so that it looks at the target: AttackObj.transform.rotation = Quaternion.LookRotation(TargetPos - AttackObj.transform.position); //Hide the attack object after some time: AttackObj.DestroyTimer = AttackSources[AttackStep].AttackObjDestroyTime; AttackObj.ShowAttackObjEffect(); //Play the attack audio: AudioManager.PlayAudio(gameObject, AttackSound, false); //----------------------------------------------------------------------------------------------- //search for the next attack object: if (AttackType == AttackTypes.InOrder) { //if the attack types is in order AttackStep++; } if (AttackStep >= AttackSources.Length || AttackType == AttackTypes.Random) { //end of attack round: //Reload the attack timer: AttackStep = 0; AttackStepTimer = AttackSources[AttackStep].AttackDelay; } else { AttackStepTimer = AttackSources[AttackStep].AttackDelay; } } } } } } } } } } } }
//a method that launches the attack object (indirect attack): public void LaunchAttackObj() { AttackObject AttackObj = ObjPool.GetEffectObj(EffectObjPool.EffectObjTypes.AttackObj, AttackSources[AttackStep].AttackObj).GetComponent <AttackObject>(); AttackObj.transform.position = AttackSources[AttackStep].AttackObjSource.transform.position; //Set the attack object's position: AttackObj.gameObject.SetActive(true); //Activate the attack object AttackObj.DefaultUnitDamage = UnitDamage; AttackObj.DefaultBuildingDamage = BuildingDamage; AttackObj.CustomDamage = CustomDamage; Vector3 TargetPos = AttackTarget.transform.position; if (TargetUnit) { AttackObj.TargetFactionID = TargetUnit.FactionID; TargetPos = TargetUnit.PlayerSelection.transform.position; } else if (TargetBuilding) { AttackObj.TargetFactionID = TargetBuilding.FactionID; TargetPos = TargetBuilding.PlayerSelection.transform.position; } AttackObj.DamageOnce = AttackSources[AttackStep].DamageOnce; AttackObj.DestroyOnDamage = AttackSources[AttackStep].DestroyAttackObjOnDamage; AttackObj.DealDamage = DealDamage; AttackObj.Source = this; AttackObj.SourceFactionID = AttackerFactionID(); if (AreaDamage == false) { AttackObj.DidDamage = false; AttackObj.DoDamage = !DirectAttack; AttackObj.AreaDamage = false; } else { AttackObj.DamageOnce = true; AttackObj.DidDamage = false; AttackObj.AreaDamage = true; AttackObj.AttackRanges = AttackRanges; } //Damage over time: AttackObj.DoT = DoT; //Attack object movement: AttackObj.MvtVector = (TargetPos - AttackSources[AttackStep].AttackObjSource.transform.position) / Vector3.Distance(AttackTarget.transform.position, AttackSources[AttackStep].AttackObjSource.transform.position); AttackObj.Speed = AttackSources[AttackStep].AttackObjSpeed; //Set the attack obj's rotation so that it looks at the target: AttackObj.transform.rotation = Quaternion.LookRotation(TargetPos - AttackObj.transform.position); //pass attack effect: AttackObj.AttackEffect = AttackEffect; AttackObj.AttackEffectTime = AttackEffectTime; //Hide the attack object after some time: AttackObj.gameObject.GetComponent <EffectObj>().Timer = AttackSources[AttackStep].AttackObjDestroyTime; AttackObj.gameObject.GetComponent <EffectObj>().EnableLifeTime = true; AttackObj.ShowAttackObjEffect(); //Attack object delay only if the attack object is supposed to be created on delay. AttackObj.DelayTime = (AttackSources[AttackStep].CreateOnDelay == true) ? AttackSources[AttackStep].DelayTime : 0.0f; AttackObj.DamageInDelay = AttackSources[AttackStep].DamageInDelay; //do damage while in delay time (if the attack object is created in delay mode)? //if there's a delay for the attack object and there's a parent object for delay: if (AttackSources[AttackStep].DelayTime > 0.0f && AttackSources[AttackStep].DelayParentObj != null) { //set delay parent object: AttackObj.transform.SetParent(AttackSources[AttackStep].DelayParentObj, true); } //Play the attack audio: AudioManager.PlayAudio(gameObject, AttackSound, false); //----------------------------------------------------------------------------------------------- //search for the next attack object: if (AttackType == AttackTypes.InOrder) { //if the attack types is in order AttackStep++; } if (AttackStep >= AttackSources.Length || AttackType == AttackTypes.Random) { //end of attack round: //Reload the attack timer: AttackStep = 0; //Only consider the delay time here if the attack object will not be created while the delay is going (if that's the case, the attack object itself will handle the delay). AttackStepTimer = (AttackSources[AttackStep].CreateOnDelay == false) ? AttackSources[AttackStep].DelayTime : 0.0f; } else { AttackStepTimer = AttackSources[AttackStep].DelayTime; } FinishAttack(); }
//a method that launches the attack object (indirect attack): public void LaunchAttackObj() { AttackTimer = AttackReload; CanPlayAnim = true; AttackObject AttackObj = ObjPool.GetEffectObj(EffectObjPool.EffectObjTypes.AttackObj, AttackSources[AttackStep].AttackObj).GetComponent <AttackObject>(); AttackObj.transform.position = AttackSources[AttackStep].AttackObjSource.transform.position; //Set the attack object's position: AttackObj.gameObject.SetActive(true); //Activate the attack object AttackObj.DefaultUnitDamage = UnitDamage; AttackObj.DefaultBuildingDamage = BuildingDamage; AttackObj.CustomDamage = CustomDamage; Vector3 TargetPos = AttackTarget.transform.position; if (TargetUnit) { AttackObj.TargetFactionID = TargetUnit.FactionID; TargetPos = TargetUnit.PlayerSelection.transform.position; } else if (TargetBuilding) { AttackObj.TargetFactionID = TargetBuilding.FactionID; TargetPos = TargetBuilding.PlayerSelection.transform.position; } AttackObj.DamageOnce = AttackSources[AttackStep].DamageOnce; AttackObj.DestroyOnDamage = AttackSources[AttackStep].DestroyAttackObjOnDamage; AttackObj.DealDamage = DealDamage; AttackObj.Source = this; AttackObj.SourceFactionID = AttackerFactionID(); if (AreaDamage == false) { AttackObj.DidDamage = false; AttackObj.DoDamage = !DirectAttack; AttackObj.AreaDamage = false; } else { AttackObj.DamageOnce = true; AttackObj.DidDamage = false; AttackObj.AreaDamage = true; AttackObj.AttackRanges = AttackRanges; } //Damage over time: AttackObj.DoT = DoT; //Attack object movement: AttackObj.MvtVector = (TargetPos - AttackSources[AttackStep].AttackObjSource.transform.position) / Vector3.Distance(AttackTarget.transform.position, AttackSources[AttackStep].AttackObjSource.transform.position); AttackObj.Speed = AttackSources[AttackStep].AttackObjSpeed; //Set the attack obj's rotation so that it looks at the target: AttackObj.transform.rotation = Quaternion.LookRotation(TargetPos - AttackObj.transform.position); //Hide the attack object after some time: AttackObj.DestroyTimer = AttackSources[AttackStep].AttackObjDestroyTime; AttackObj.ShowAttackObjEffect(); //Play the attack audio: AudioManager.PlayAudio(gameObject, AttackSound, false); //----------------------------------------------------------------------------------------------- //search for the next attack object: if (AttackType == AttackTypes.InOrder) { //if the attack types is in order AttackStep++; } if (AttackStep >= AttackSources.Length || AttackType == AttackTypes.Random) { //end of attack round: //Reload the attack timer: AttackStep = 0; AttackStepTimer = AttackSources[AttackStep].AttackDelay; } else { AttackStepTimer = AttackSources[AttackStep].AttackDelay; } //If this not the basic attack then revert back to the basic attack after done if (BasicAttack == false && MultipleAttacksMgr != null) { MultipleAttacksMgr.EnableAttackType(MultipleAttacksMgr.BasicAttackID); } //Cooldown: StartCoolDown(); //Attack once? cancel attack to prevent source from attacking again if (AttackOnce == true) { CancelAttack(); } ReloadAttackDelay(); }