//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); }
/// <summary> /// Launches an attack on the NPC faction's current target faction. /// </summary> /// <returns>True if the attack has been successfully launched, otherwise false.</returns> public bool LaunchAttack() { //making sure there's a valid target faction or we're in the peace time if (targetFaction == null || gameMgr.InPeaceTime()) { return(false); } //mark as attacking: IsAttacking = true; RefreshCurrentAttackUnits(); //we'll be searching for the next building to attack starting from the last attack pos, initially set it as the capital building lastAttackPos = gameMgr.GetFaction(factionMgr.FactionID).CapitalPosition; currentTarget = null; //pick a target building: SetTargetEntity(targetFaction.GetBuildings().Cast <FactionEntity>(), true); //start the attack order timer: attackOrderTimer = attackOrderReloadRange.getRandomValue(); return(true); }
//a method that picks a target entity to attack: private bool SetTargetEntity(IEnumerable <FactionEntity> factionEntities) { //search the target faction's entities and see if there's a match: float lastDistance = 0; //we wanna get the closest entity to the last attack position: foreach (FactionEntity entity in factionEntities) { //if the building is valid and not destroyed yet if (entity != null && entity.EntityHealthComp.IsDestroyed == false) { //and the building's code matches or this is an eliminate all game: if (gameMgr.GetDefeatCondition() == DefeatConditions.eliminateAll || targetBuildingCodes.Contains(entity.GetCode())) { //get the closest building: if (currentTarget == null || Vector3.Distance(currentTarget.transform.position, lastAttackPos) < lastDistance) { currentTarget = entity; lastDistance = Vector3.Distance(entity.transform.position, lastAttackPos); } } } } return(currentTarget != null); }
//method to launch the attack: void LaunchAttack() { //making sure there's a valid target faction: if (targetFaction == null) { return; } //mark as attacking: isAttacking = true; //clear the current attack units list: currentAttackUnits.Clear(); currentAttackUnits.AddRange(factionMgr.GetAttackUnits(1 - npcMgr.defenseManager_NPC.defenseRatioRange.getRandomValue())); //get the required units for this attack. //we'll be searching for the next building to attack starting from the last attack pos, initially set it as the capital building lastAttackPos = gameMgr.GetFaction(factionMgr.FactionID).CapitalPosition; currentTarget = null; //pick a target building: SetTargetEntity(targetFaction.GetBuildings().Cast <FactionEntity>()); //start the attack order timer: attackOrderTimer = attackOrderReloadRange.getRandomValue(); }
//a method called when the faction entity's health hits null: public virtual void OnZeroHealth(int value, FactionEntity source) { //set it back to 0.0f as we don't allow negative health values. CurrHealth = 0; //if the faction entity isn't already dead: if (isDead == false) { isDead = true; //mark as dead KilledBy = source; //assign the source that caused the death of this faction entity //is there a valid source that caused the death of this faction entity? if (source != null) { //award the destroy award to the source if the source is not the same faction ID: if (destroyAward.Length > 0 && source.FactionID != factionEntity.FactionID) { for (int i = 0; i < destroyAward.Length; i++) { //award destroy resources to source: gameMgr.ResourceMgr.UpdateResource(source.FactionID, destroyAward[i].Name, destroyAward[i].Amount); } } } //destroy the faction entity DestroyFactionEntity(false); } }
/// <summary> /// Sets the current target of the NPC faction to the closest from an enumerable of FactionEntity instances. /// </summary> /// <param name="factionEntities">The enumerable of FactionEntity instances to pick a target from.</param> /// <param name="clearCurrentTarget">False if you want to keep the current target if no valid target is found.</param> /// <returns>True if the target has been successfully set, otherwise false.</returns> public bool SetTargetEntity(IEnumerable <FactionEntity> factionEntities, bool clearCurrentTarget) { if (clearCurrentTarget) { ResetCurrentTarget(); } //search the target faction's entities and see if there's a match: float lastDistance = 0; //we wanna get the closest entity to the last attack position: foreach (FactionEntity entity in factionEntities) { if (!IsValidTarget(entity)) //can't be a valid target? { continue; //move to next one } //get the closest possible entity if (currentTarget == null || Vector3.Distance(currentTarget.transform.position, lastAttackPos) < lastDistance) { currentTarget = entity; lastDistance = Vector3.Distance(entity.transform.position, lastAttackPos); } } return(currentTarget != null); }
public override void Init(GameManager gameMgr, FactionEntity source) { base.Init(gameMgr, source); unit = (Unit)source; CurrHealth = MaxHealth; //unit has maximum health by default }
public void UpdateTaskLauncherTasks(FactionEntity sourceEntity, TaskLauncher taskLauncher) { if (taskLauncher == null || taskLauncher.Initiated == false || taskLauncher.GetTasksCount() == 0) //if the task launcher is invalid or the source can't manage a task { return; } for (int taskID = 0; taskID < taskLauncher.GetTasksCount(); taskID++) //go through all tasks { FactionEntityTask task = taskLauncher.GetTask(taskID); if (task.IsEnabled() == true) { TaskUI taskUI = Add(new TaskUIAttributes { ID = taskID, type = task.GetTaskType(), icon = task.GetIcon(), source = sourceEntity, taskLauncher = taskLauncher }, task.GetTaskPanelCategory()); if (task.GetTaskType() == TaskTypes.createUnit) //if this is a unit creation task, check if it has reached its limit and change task ui image color accordinly { taskUI.GetComponent <Image>().color = sourceEntity.FactionMgr.HasReachedLimit(task.UnitCode, task.UnitCategory) == true ? Color.red : Color.white; } } } UpdateInProgressTasks(taskLauncher); //show the in progress tasks }
//trigger unit/building upgrades locally: private void LaunchTriggerUpgrades(IEnumerable <Upgrade> upgrades, FactionEntity upgradeLauncher) { foreach (Upgrade u in upgrades) { LaunchUpgrade(u, 0, upgradeLauncher, false); //will trigger the upgrade for the first target only! } }
//when the building's health is updated public override void OnHealthUpdated(int value, FactionEntity source) { base.OnHealthUpdated(value, source); CustomEvents.OnBuildingHealthUpdated(building, value, source); CheckState(value > 0); //check the building's state }
//a method that launches the attack movement of a list of units public void LaunchAttack(List <Unit> units, FactionEntity targetEntity, Vector3 targetPosition, AttackModes attackMode, bool playerCommand) { if (units.Count == 1) //one unit only? { //use the one-unit attack movement LaunchAttack(units[0], targetEntity, targetPosition, attackMode, playerCommand); return; } if (GameManager.MultiplayerGame == false) //single player game, directly prepare the unit's attack movement { PrepareMove(units, targetPosition, targetEntity ? targetEntity.GetRadius() : 0.0f, targetEntity, InputMode.attack, playerCommand, true, attackMode, targetEntity); } else if (RTSHelper.IsLocalPlayer(units[0]) == true) //multiplayer game and this is the local player { //send input action to the input manager NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.unitGroup, targetMode = (byte)InputMode.attack, targetPosition = targetPosition, value = (int)attackMode, groupSourceID = InputManager.UnitListToString(units) }; //sent input InputManager.SendInput(newInput, null, targetEntity); } }
public void Init(FactionEntity factionEntity) { this.factionEntity = factionEntity; AttackEntities = GetComponents <AttackEntity>(); //get all attack entity components attached to the faction entity if (AttackEntities.Length < 1) //if there's no more than one attack entity component { Destroy(this); //no need to have this component return; } activeAttack = null; for (int i = 0; i < AttackEntities.Length; i++) //go through the attack components { AttackEntities[i].ID = i; //set the attack entity ID for each attack component (will be used by UI task buttons to refer to the attack components). if (AttackEntities[i].IsBasic() == true) //if this is the basic attack { BasicAttackID = i; SetActiveAttack(i); } else { AttackEntities[i].Toggle(false); } } }
//check if the attacker can engage target: public ErrorMessage CanEngageTarget(FactionEntity potentialTarget) { if (gameMgr.InPeaceTime() == true) { return(ErrorMessage.peaceTime); } else if (potentialTarget == null) //if there's no target assigned { return(!requireTarget ? ErrorMessage.none : ErrorMessage.attackTargetRequired); //see if the attack entity can attack without target } else if (potentialTarget.FactionID == FactionEntity.FactionID && engageFriendly == false) { return(ErrorMessage.targetSameFaction); } else if (potentialTarget.EntityHealthComp.CanBeAttacked == false) //peace time, same faction ID or target can't be attacked? -> nope { return(ErrorMessage.targetNoAttack); } if (engageAllTypes) //attack all types then yes! { return(ErrorMessage.none); } //if the target can't attack units/buildings and this is the case then nope if ((potentialTarget.Type == EntityTypes.building && engageBuildings == false) || (potentialTarget.Type == EntityTypes.unit && engageUnits == false)) { return(ErrorMessage.entityNotAllowed); } return(codesList.Contains(potentialTarget.GetCode(), potentialTarget.GetCategory()) == engageInList ? ErrorMessage.none : ErrorMessage.entityNotAllowed); }
//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 } }
//called each time a unit/building is dead, only called when the mission type is set to "eliminate" or "produce" private void OnFactionEntityEvent (FactionEntity factionEntity) { //if the faction entity is dead and there are entities that the player is supposed to defend if(factionEntity.FactionID == GameManager.PlayerFactionID //only if the dead faction entity belongs to the player faction && defendFactionEntities.Length > 0 && factionEntity.EntityHealthComp.IsDead()) { foreach(CodeCategoryField codeCategory in defendFactionEntities) //go through the entities that shouldn't be dead { if (codeCategory.Contains(factionEntity.GetCode(), factionEntity.GetCategory())) //if the code/category matches { Forfeit(); //mission failed return; } } } if((factionEntity.EntityHealthComp.IsDead() && type == Type.eliminate && factionEntity.FactionID != GameManager.PlayerFactionID) //if this is an elimination mission and the entity doesn't belong to the player's faction || (!factionEntity.EntityHealthComp.IsDead() && type == Type.produce && factionEntity.FactionID == GameManager.PlayerFactionID)) //produce mission type and the entity belongs to the player faction foreach (CodeCategoryField codeCategory in targetCode) //go through all assigned codes { if (codeCategory.Contains(factionEntity.GetCode(), factionEntity.GetCategory())) //if the code/category matches { OnProgress(1); //positive mission progress break; } } }
//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 } } }
public void UpdateMultipleAttackTasks(FactionEntity sourceEntity, MultipleAttackManager multipleAttackComp) { if (multipleAttackComp == null) { return; } for (int attackID = 0; attackID < multipleAttackComp.AttackEntities.Length; attackID++) //go through all the attack entities { AttackEntity attackComp = multipleAttackComp.AttackEntities[attackID]; if (!attackComp.IsLocked && !attackComp.IsActive()) //as long as the attack entity is not active and not locked, show a task to activate it: { TaskUI taskUI = Add(new TaskUIAttributes { ID = attackID, type = TaskTypes.attackTypeSelection, icon = attackComp.GetIcon(), source = sourceEntity }, multipleAttackComp.GetTaskPanelCategory()); if (attackComp.CoolDownActive == true) //if the attack type is in cool down mode { Color nextColor = taskUI.GetComponent <Image>().color; nextColor.a = 0.5f; //make it semi-transparent to indicate cooldown to player taskUI.GetComponent <Image>().color = nextColor; } } } }
private bool triggerAnimationInDelay = false; //true => the attack animation is triggered when the delay starts. if false, it will only be triggered when the attack is triggered public override void Init(GameManager gameMgr, FactionEntity factionEntity, MultipleAttackManager multipleAttackMgr) { base.Init(gameMgr, factionEntity, multipleAttackMgr); this.unit = (Unit)factionEntity; RangeType = gameMgr.AttackMgr.GetRangeType(rangeTypeCode); }
//called each time a faction entity switches its attack: private void OnAttackSwitch(AttackEntity attackEntity, FactionEntity target, Vector3 targetPosition) { //only if the source faction entity is the only player entity selected if (SelectionManager.IsSelected(attackEntity.FactionEntity.GetSelection(), true, true)) { Update(); } }
public bool IsFree() //is the entity managed by this selection component a faction entity or a free one? { if (FactionEntity != null) { return(FactionEntity.IsFree()); } return(false); }
/// <summary> /// Tests whether a FactionEntity can be a potential target for the NPC faction. /// </summary> /// <param name="potentialTarget">The FactionEntity instance to test.</param> /// <returns>True if the potential target can be assigned as a target, otherwise false.</returns> public bool IsValidTarget(FactionEntity potentialTarget) { return(potentialTarget != null && //must be valid !potentialTarget.EntityHealthComp.IsDead() && //must be still alive targetFaction != null && //there must be a target faction potentialTarget.FactionID == targetFaction.FactionID //factionID must match the target faction's ID //if the entity can be targeted by the NPC faction or this is an eliminate all game: && (gameMgr.GetDefeatCondition() == DefeatConditions.eliminateAll || targetPicker.IsValidTarget(potentialTarget))); }
//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. } }
//the method that updates the health bar of the unit/building that the player has their mouse on. public void Update(FactionEntity source) { if (enabled == false || source != currSource) //if the hover health bar is disabled { return; } healthBar.Update(source.EntityHealthComp.CurrHealth / (float)source.EntityHealthComp.MaxHealth); }
public virtual void Init(GameManager gameMgr, FactionEntity source) { this.gameMgr = gameMgr; factionEntity = source; //get the Faction Entity component. //initial settings: isDead = false; //faction entity is not dead by default. DotComps = new List <DamageOverTime>(); }
//a method that upgrades a faction entity instance locally public void UpgradeInstance(FactionEntity instance, FactionEntity target, int factionID, EffectObj upgradeEffect) { switch (instance.Type) { case EntityTypes.building: Building currBuilding = instance as Building; Unit[] currBuilders = currBuilding.WorkerMgr.GetAll(); //get the current builders of this building if there are any foreach (Unit unit in currBuilders) //and make them stop building the instance of the building since it will be destroyed. { unit.BuilderComp.Stop(); } //create upgraded instance of the building Building upgradedBuilding = gameMgr.BuildingMgr.CreatePlacedInstanceLocal( (Building)target, instance.transform.position, target.transform.rotation.eulerAngles.y, ((Building)instance).CurrentCenter, factionID, currBuilding.IsBuilt, //depends on the current state of the instance to destroy ((Building)instance).FactionCapital); foreach (Unit unit in currBuilders) { unit.BuilderComp.SetTarget(upgradedBuilding); } CustomEvents.OnBuildingInstanceUpgraded((Building)instance); //trigger custom event break; case EntityTypes.unit: //create upgraded instance of the unit gameMgr.UnitMgr.CreateUnit( (Unit)target, instance.transform.position, instance.transform.rotation, instance.transform.position, factionID, null); CustomEvents.OnUnitInstanceUpgraded((Unit)instance); //trigger custom event break; } //if there's a valid upgrade effect assigned: if (upgradeEffect != null) { //show the upgrade effect for the player: gameMgr.EffectPool.SpawnEffectObj(upgradeEffect, instance.transform.position, upgradeEffect.transform.rotation); } instance.EntityHealthComp.DestroyFactionEntity(true); //destroy the instance }
public void Init(GameManager gameMgr, FactionEntity source) { this.gameMgr = gameMgr; FactionEntity = source; if (interactionPosition == null) //no interaction position is assigned { interactionPosition = transform; //assign the interaction position to the faction entity's position } currCapacity = 0; }
//a method called by the Selection Manager to attempt to select the object associated with this selection entity public virtual void OnSelected() { if (FactionEntity) //if this is a faction entity { FactionEntity.OnMouseClick(); //trigger a mouse click in the faction entity's main component } AudioManager.Play(gameMgr.GetGeneralAudioSource(), Source.GetSelectionAudio(), false); //play the selection audio Source.EnableSelectionPlane(); //enable the selection plane of the source IsSelected = true; CustomEvents.OnEntitySelected(Source); }
//when the building reaches its max health: public override void OnMaxHealthReached(int value, FactionEntity source) { base.OnMaxHealthReached(value, source); Unit[] workersList = building.WorkerMgr.GetAll(); //get all workers in the worker manager for (int i = 0; i < workersList.Length; i++) { workersList[i].BuilderComp.Stop(); //stop constructing } CompleteConstruction(); }
//a method called when the faction entity's health has been updated: public virtual void OnHealthUpdated(int value, FactionEntity source) { if (value < 0.0) { //if this is the local player's faction ID and the attack warning manager is active: if (factionEntity.FactionID == GameManager.PlayerFactionID) { gameMgr.AttackWarningMgr?.Add(transform.position); //show attack warning on minimap } } CustomEvents.OnFactionEntityHealthUpdated(factionEntity, value, source); //trigger custom event }
//a method that returns the damage that is supposed to be dealt to a target. public int GetDamage(FactionEntity target) { foreach (CustomDamage cd in customDamages) //see if the target's code is in the custom damages list { if (cd.code.Contains(target.GetCode(), target.GetCategory())) //if the target is found then pick the custom damage { return(cd.damage); } } //no custom damage? pick either the damage for units or for buildings return(target.Type == EntityTypes.unit ? unitDamage : buildingDamage); }