//a method called when the unit's health has been updated: public override void OnHealthUpdated(int value, FactionEntity source) { base.OnHealthUpdated(value, source); CustomEvents.OnUnitHealthUpdated(unit, value, source); if (value < 0) //if the unit's health has been decreased { unit.SetAnimState(UnitAnimState.takingDamage); //set the animator state to taking damage. if (stopMovingOnDamage == true) //stop player movement on damage if this is set to true { unit.MovementComp.Stop(); } if (enableDamageAnimation == true) //if the damage animation is enabled { damageAnimationTimer = damageAnimationDuration; //start the timer. damageAnimationActive = true; //mark as active } if (source != null && (GameManager.MultiplayerGame == false || RTSHelper.IsLocalPlayer(unit) == true)) //if the attack source is known and this is either a single player game or the local player's unit { //if the unit has an attack component and it can attack back and it is idle if (unit.AttackComp != null && unit.AttackComp.IsActive() && unit.AttackComp.CanEngageWhenAttacked() == true && unit.IsIdle() == true) { gameMgr.MvtMgr.LaunchAttack(unit, source, source.GetSelection().transform.position, MovementManager.AttackModes.none, false); //launch attack at the source } TriggerEscapeOnAttack(); //attempt to trigger the escape on attack behavior if it is enabled } } }
//cancel an in progress task of this type public void Cancel() { switch (type) { case TaskTypes.createUnit: //update the population slots gameMgr.GetFaction(factionEntity.FactionID).UpdateCurrentPopulation(-UnitPopulationSlots); //update the limits list: factionEntity.FactionMgr.UpdateLimitsList(UnitCode, UnitCategory, false); break; } gameMgr.ResourceMgr.UpdateRequiredResources(requiredResources, true, factionEntity.FactionID); //Give back the task resources. cancelEvent.Invoke(); //trigger unity event. //if the task was supposed to be used once but is cancelled: if (useMode != UseMode.multiple) { IsAvailable = true; //make it available again. if (useMode == UseMode.onceAllInstances) //if this was marked as usable once for all instances. { gameMgr.TaskMgr.Status.ToggleTask(code, factionEntity.FactionID, true); } } if (factionEntity.FactionID == GameManager.PlayerFactionID && factionEntity.GetSelection().IsSelected) //if this is the local player and the faction entity is selected { AudioManager.Play(gameMgr.GetGeneralAudioSource(), cancelAudio, false); } }
//search for a target to attack private void SearchTarget() { //first pick the search center and size/range (if this is an AI unit defending a range center, the search range and size are that of the building center). float searchSize = (SearchRangeCenter == null) ? searchRange : SearchRangeCenter.Size; Vector3 searchCenter = (SearchRangeCenter == null) ? transform.position : SearchRangeCenter.transform.position; FactionEntity potentialTarget = null; //this will hold the potential target faction entity float currDistance = 0.0f; //go through the enemy faction entities of the attack entity's faction foreach (FactionEntity enemyEntity in (FactionEntity.IsFree() == true || engageFriendly) ? gameMgr.UnitMgr.GetAllUnits() : FactionEntity.FactionMgr.GetEnemyFactionEntities()) { if (enemyEntity == null || enemyEntity.enabled == false || enemyEntity.EntityHealthComp.IsDead() == true || enemyEntity == FactionEntity as Unit) { continue; //move to the next enemy faction entity in case the Unit's component is not enabled or the entity isn't alive } //if the enemy faction entity is inside the current search size and it can be attacked if ((currDistance = Vector3.Distance(enemyEntity.transform.position, searchCenter)) < searchSize && CanEngageTarget(enemyEntity) == ErrorMessage.none) { searchSize = currDistance; //this will always decrease the search size to the closest enemy entity potentialTarget = enemyEntity; } } if (potentialTarget != null) { SetTarget(potentialTarget, potentialTarget.GetSelection().transform.position); //if there's a potential target found, set it as next target } }
//attack the assigned target. void AttackTarget() { //making sure that the NPC faction in an attack in progress and that there's a valid target: if (currentTarget == null || isAttacking == false) { return; } MovementManager.AttackModes attackMode = MovementManager.AttackModes.none; Building targetBuilding = currentTarget.Type == EntityTypes.building ? (Building)currentTarget : null; //is this target a building? //if the current target is a building and it is being constructed: if (targetBuilding != null && targetBuilding.WorkerMgr.currWorkers > 0) { Unit[] workersList = targetBuilding.WorkerMgr.GetAll(); //get all workers in the worker manager //attack the workers first: go through the workers positions for (int i = 0; i < workersList.Length; i++) { //find worker: if (workersList[i] != null && workersList[i].BuilderComp.IsInProgress() == true) { //assign it as target. currentTarget = workersList[i]; //force attack units to attack it: attackMode = MovementManager.AttackModes.change; } } } //launch the actual attack: gameMgr.MvtMgr.LaunchAttack(currentAttackUnits, currentTarget, currentTarget.GetSelection().transform.position, attackMode, false); }
//health related events: private void OnFactionEntityHealthUpdated(FactionEntity factionEntity, int value, FactionEntity source) { hoverHealthBar.Update(factionEntity); //update the hover health bar in case this unit is selected if (factionEntity.GetSelection().IsSelectedOnly) //if this is the only selected entity { singleSelection.UpdateFactionEntityHealthUI(factionEntity); //update the health UI. } }
//a method that destroys a faction entity locally public virtual void DestroyFactionEntityLocal(bool upgrade) { gameMgr.SelectionMgr.Selected.Remove(factionEntity); //deselect the faction entity if it was selected //faction entity death: isDead = true; CurrHealth = 0; //remove the minimap icon: factionEntity.GetSelection().DisableMinimapIcon(); //If this is no upgrade if (upgrade == false) { factionEntity.Disable(true); CustomEvents.OnFactionEntityDead(factionEntity); //call the custom events if (destructionEffect != null) //do not show desctruction effect if it's not even assigned { //get the destruction effect from the pool EffectObj newDestructionEffect = gameMgr.EffectPool.SpawnEffectObj(destructionEffect, transform.position, Quaternion.identity); //destruction sound effect if (destructionAudio != null) { //Check if the destruction effect object has an audio source: if (newDestructionEffect.GetComponent <AudioSource>() != null) { AudioManager.Play(newDestructionEffect.GetComponent <AudioSource>(), destructionAudio, false); //play the destruction audio } else { Debug.LogError("A destruction audio clip has been assigned but the destruction effect object doesn't have an audio source!"); } } } } //Destroy the faction entity's object: if (destroyObject == true) //only if object destruction is allowed { Destroy(gameObject, !upgrade ? destroyObjectTime : 0.0f); IsDestroyed = true; } }
//called when a unit requests support from nearby units: void OnUnitSupportRequest(Vector3 pos, FactionEntity target) { //if the unit support feature is disabled if (unitSupportEnabled == false) { return; //do not proceed. } //go through the faction's attack units: foreach (Unit u in factionMgr.GetAttackUnits()) { //check if the unit is within the required distance: if (Vector3.Distance(u.transform.position, pos) < unitSupportRange.getRandomValue()) { //request support (as long as the unit isn't busy): gameMgr.MvtMgr.LaunchAttack(u, target, target.GetSelection().transform.position, MovementManager.AttackModes.none, false); } } }
/// <summary> /// Called when a faction entity requests support from nearby NPC units /// </summary> /// <param name="position">The position where the support request has been initiated.</param> /// <param name="target">The target that has to be eliminated.</param> /// <returns>True if the support request has successfully processed, otherwise false.</returns> public bool OnUnitSupportRequest(Vector3 position, FactionEntity target) { //if the unit support feature is disabled or the target is invalid if (!unitSupportEnabled || target == null) { return(false); //do not proceed. } //pick the next support range: float nextSupportRange = unitSupportRange.getRandomValue(); //get attack units inside the chosen support range List <Unit> inRangeAttackUnits = factionMgr.GetAttackUnits() .Where(unit => Vector3.Distance(unit.transform.position, position) <= nextSupportRange).ToList(); if (inRangeAttackUnits.Count > 0) { //request support (as long as the unit isn't busy): gameMgr.MvtMgr.LaunchAttack(inRangeAttackUnits, target, target.GetSelection().transform.position, MovementManager.AttackModes.none, false); } return(true); }
/// <summary> /// Orders the NPC faction to engage its currently set target. /// </summary> public void EngageCurrentTarget() { if (!IsAttacking || //not actively attacking? currentTarget == null || //has a current target? targetFaction == null || //no target faction assigned? gameMgr.InPeaceTime() || //or still in peace time? currentAttackUnits.Count == 0) //or no more attack units? { return; } MovementManager.AttackModes attackMode = MovementManager.AttackModes.none; //if the current target is a building and it is being constructed: if (currentTarget.Type == EntityTypes.building && (currentTarget as Building).WorkerMgr.currWorkers > 0) { Unit[] workersList = (currentTarget as Building).WorkerMgr.GetAll(); //get all workers in the worker manager //attack the workers first: go through the workers positions for (int i = 0; i < workersList.Length; i++) { //find worker: if (workersList[i] != null && workersList[i].BuilderComp.IsInProgress() == true) { //assign it as target. currentTarget = workersList[i]; //force attack units to attack it: attackMode = MovementManager.AttackModes.change; } } } //launch the actual attack: gameMgr.MvtMgr.LaunchAttack( //if only idle units can be sent to attack, make sure this is the case. sendOnlyIdle ? currentAttackUnits.Where(unit => unit.IsIdle()).ToList() : currentAttackUnits , currentTarget, currentTarget.GetSelection().transform.position, attackMode, false); }
} //the current attack target //the target position is either the assigned target's current position or the last registered target position public Vector3 GetTargetPosition() { return(Target ? Target.GetSelection().transform.position : lastTargetPosition); }