//a method to trigger the escape on attack behavior public void Trigger() { if (isActive == false) //do not proceed if the component is not active { return; } Vector3 targetPosition = gameMgr.MvtMgr.GetRandomMovablePosition(transform.position, range.getRandomValue(), unit, unit.MovementComp.GetAgentAreaMask()); //find a random position to escape to if (GameManager.MultiplayerGame == false) //single player game { TriggerLocal(targetPosition); } else if (RTSHelper.IsLocalPlayer(unit) == true) //multiplayer game and this is the local player { NetworkInput newInput = new NetworkInput() //create new input for the escape task { sourceMode = (byte)InputMode.unit, targetMode = (byte)InputMode.unitEscape, initialPosition = transform.position, targetPosition = targetPosition }; InputManager.SendInput(newInput, unit, null); //send input to input manager } }
//enable an attack entity public ErrorMessage EnableAttack(int ID) { //if the attack type is locked then it can't be activated if (AttackEntities[ID].IsLocked) { return(ErrorMessage.attackTypeLocked); } else if (AttackEntities[ID].CoolDownActive == true) //if the target attack type is in cool down right now: { return(ErrorMessage.attackInCooldown); } if (GameManager.MultiplayerGame == false) //single player game -> directly enable attack entity { EnableAttackLocal(ID); } else if (RTSHelper.IsLocalPlayer(activeAttack.FactionEntity)) { NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.factionEntity, targetMode = (byte)InputMode.multipleAttack, value = ID }; InputManager.SendInput(newInput, factionEntity, null); //send input to input manager } return(ErrorMessage.none); }
//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); } }
//the Move method is called when another component requests the movement of a list of units public void Move(List <Unit> units, Vector3 destination, float offsetRadius, Entity target, InputMode targetMode, bool playerCommand) { if (units.Count == 1) //if there's only one unit in the list { Move(units[0], destination, offsetRadius, target, targetMode, playerCommand); //use the one-unit movement return; } if (GameManager.MultiplayerGame == false) //single player game, directly prepare the unit's list movement { PrepareMove(units, destination, offsetRadius, target, targetMode, playerCommand); } 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)targetMode, targetPosition = destination, value = Mathf.FloorToInt(offsetRadius), groupSourceID = InputManager.UnitListToString(units) }; //sent input InputManager.SendInput(newInput, null, target); } }
//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 } } }
//a method that stops the unit's movement. public void Stop() { DisablePendingMovement(true); AudioManager.Stop(unit.AudioSourceComp); //stop the movement audio from playing if (unit.HealthComp.IsDead() == true || isMoving == false) //if the unit is already dead or not moving, do not proceed. { return; } SetMaxSpeed(speed); //set the movement speed to the default one in case it was changed by the Attack on Escape component. if (!useNavAgent) //if we're using the NavAgent to move the unit, it will handle keeping the height of the unit in check { StopCoroutine(heightCheckCoroutine); } isMoving = false; //marked as not moving unit.SetAnimState(UnitAnimState.idle); //get into idle state //unit doesn't have a target APC or Portal to move to anymore targetAPC = null; targetPortal = null; rotationTarget = RTSHelper.GetLookRotation(transform, lookAtTarget); //update the rotation target using the registered lookAt position. UpdateTargetPositionCollider(transform.position); //set the target position's collider to the current unit position since the movement has stopped. }
void OnTriggerEnter(Collider other) { //delay timer is still going and we can't damage in delay time + make sure that no damage has been applied or damaging multiple times is enabled if (isActive == false || effectObjComp.CurrentState != EffectObj.State.running || delayTimer > 0.0f && damageInDelay == false && (didDamage == false || damageOnce == false)) { return; } if (RTSHelper.IsInLayerMask(obstacleLayerMask, other.gameObject.layer)) //Check if this is an obstacle that stops the attack object. { ApplyDamage(other.gameObject, null, transform.position); return; } SelectionEntity hitSelection = other.gameObject.GetComponent <SelectionEntity>(); //if the attack object didn't enter in collision with a selection entity or it did but it was one belonging to resource if (hitSelection == null || hitSelection.FactionEntity == null) { return; } //make sure the faction entity belongs to an enemy faction and that it's not dead yet: if ((hitSelection.FactionEntity.FactionID != sourceFactionID || damageFriendly) && hitSelection.FactionEntity.EntityHealthComp.IsDead() == false) { ApplyDamage(other.gameObject, hitSelection.FactionEntity, hitSelection.transform.position); } }
//update the peace time text to display the current peace time: public void Update(float currTime) { if (timeText == null || currTime <= 0.0f) { return; //do not proceed if the peace time text is not assigned or the peace time is invalid } timeText.text = RTSHelper.TimeToString(currTime); }
//a method to collect resources. public void CollectResources(int generatorID) { generators[generatorID].OnResourceCollected(building.FactionID); //collect the resources and reset the generator's settings if (RTSHelper.IsLocalPlayer(building)) //if this is the local player: { CustomEvents.OnResourceGeneratorCollected(this, generatorID); //trigger custom event AudioManager.Play(gameMgr.GetGeneralAudioSource(), collectionAudio, false); //plau the collection audio } }
//a method that initializes a single player game: private bool InitSinglePlayerGame() { //if there's no single player manager then if (LobbyManager.instance == null) { return(false); //do not proceed. } //If there's a map manager script in the scene, it means that we just came from the single player menu, so we need to set the NPC players settings! //randomzie the faction slots List <int> factionSlots = RTSHelper.GenerateIndexList(factions.Count); RTSHelper.ShuffleList <int>(factionSlots); //randomize the faction slots indexes list IDs by shuffling it. RandomizeFactionSlots(factionSlots.ToArray()); //This where we will set the NPC settings using the info from the single player manager: //First check if we have enough faction slots available: if (LobbyManager.instance.LobbyFactions.Count <= factions.Count) { defeatCondition = LobbyManager.instance.UIMgr.defeatConditionMenu.GetValue(); //set defeat condition speedModifier = LobbyManager.instance.UIMgr.speedModifierMenu.GetValue(); //set speed modifier //loop through the factions slots of this map: for (int i = 0; i < LobbyManager.instance.LobbyFactions.Count; i++) { factions[i].Init( LobbyManager.instance.LobbyFactions[i].GetFactionName(), LobbyManager.instance.LobbyFactions[i].GetFactionType(), LobbyManager.instance.LobbyFactions[i].GetFactionColor(), LobbyManager.instance.LobbyFactions[i].PlayerControlled, LobbyManager.instance.GetCurrentMap().GetInitialPopulation(), LobbyManager.instance.LobbyFactions[i].GetNPCType(), gameObject.AddComponent <FactionManager>(), i, this); } //if there are more slots than required. while (LobbyManager.instance.LobbyFactions.Count < factions.Count) { //remove the extra slots: factions[factions.Count - 1].InitDestroy(); factions.RemoveAt(factions.Count - 1); } //Destroy the map manager script because we don't really need it anymore: DestroyImmediate(LobbyManager.instance.gameObject); return(true); } else { Debug.LogError("[Game Manager]: Not enough slots available for all the factions coming from the single player menu."); return(false); } }
//called by the server when the game is about to start: public void OnStartGame() { if (isLocalPlayer == false || manager.IsHost == false) //the host must be the only one able to start the game { return; } List <int> factionSlots = RTSHelper.GenerateIndexList(manager.maxConnections); RTSHelper.ShuffleList <int>(factionSlots); //randomize the faction slots list IDs by shuffling it RpcOnGameStart(factionSlots.ToArray()); //let all clients know that the game is starting }
//called to activate the border public void ActivateBorder() { //make sure to get the game manager and resource manager components GameMgr = GameManager.Instance; ResourceMgr = GameMgr.ResourceMgr; //if the border is not active yet if (IsActive == false) { //shuffle building defs list: RTSHelper.ShuffleList <BuildingsInsideBorderVars>(BuildingsInsideBorder); //if we're allowed to spawn the border object if (SpawnBorderObj == true) { //create and spawn it BorderObj = (GameObject)Instantiate(BorderObj, new Vector3(transform.position.x, BorderHeight, transform.position.z), Quaternion.identity); BorderObj.transform.localScale = new Vector3(Size * BorderSizeMultiplier, BorderObj.transform.localScale.y, Size * BorderSizeMultiplier); BorderObj.transform.SetParent(transform, true); //Set the border's color to the faction it belongs to: Color FactionColor = GameMgr.Factions[MainBuilding.FactionID].FactionColor; BorderObj.GetComponent <MeshRenderer>().material.color = new Color(FactionColor.r, FactionColor.g, FactionColor.b, BorderColorTransparency); //Set the border's sorting order: BorderObj.GetComponent <MeshRenderer>().sortingOrder = GameMgr.LastBorderSortingOrder; GameMgr.LastBorderSortingOrder--; } //Add the border to all borders list: GameMgr.AllBorders.Add(this); CheckBorderResources(); //check the resources around the border IsActive = true; //mark the border as active //Custom Event: if (GameMgr.Events) { GameMgr.Events.OnBorderActivated(this); } } //set the faction manager FactionMgr = GameMgr.Factions[MainBuilding.FactionID].FactionMgr; //check the buildings in the border CheckBuildingsInBorder(); }
public void Init(GameManager gameMgr, Building building) { this.gameMgr = gameMgr; this.building = building; foreach (Generator g in generators) //go through all the available generators { g.Init(gameMgr); //init the generator's settings } if (GameManager.MultiplayerGame == true && !RTSHelper.IsLocalPlayer(building)) //if it's a multiplayer game and this does not belong to the local player's faction. { enabled = false; //disable this component } IsActive = true; }
//a method called when the resource object is to be destroyed public void DestroyResource(Unit source) { if (GameManager.MultiplayerGame == false) //if this is a single player game -> go ahead directly { DestroyResourceLocal(source); } else if (RTSHelper.IsLocalPlayer(source)) //multiplayer game and the resource collector belongs to the local faction { NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.destroy, targetMode = (byte)InputMode.resource, }; InputManager.SendInput(newInput, this, source); } }
//a method that is used to move the unit to its initial position after it spawns: public void SetInitialTargetPosition(Building source, Vector3 gotoPosition) { Creator = source; //set the building creator //only if the is owned by the local player or this is not a multiplayer game if (RTSHelper.IsLocalPlayer(this) || GameManager.MultiplayerGame == false) { if (Creator != null) //if the creator is assigned { Creator.SendUnitToRallyPoint(this); //send unit to rally point } else if (Vector3.Distance(gotoPosition, transform.position) > gameMgr.MvtMgr.GetStoppingDistance()) //only if the goto position is not within the stopping distance of this unit { gameMgr.MvtMgr.Move(this, gotoPosition, 0.0f, null, InputMode.movement, false); //no creator building? move player to its goto position } } }
//add/remove to/from the resource public void AddAmount(int value, Unit source) { if (GameManager.MultiplayerGame == false) //if this is a single player game -> go ahead directly { AddAmountLocal(value, source); } else if (RTSHelper.IsLocalPlayer(source)) //multiplayer game and the resource collector belongs to the local faction { NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.resource, targetMode = (byte)InputMode.health, value = value }; InputManager.SendInput(newInput, this, source); } }
//update when the unit doesn't have a target protected virtual void OnInactiveUpdate() { if (inProgress == true) //if the unit doesn't have a target but its job is marked as in progress { Stop(); //cancel job } if ((GameManager.MultiplayerGame == false || RTSHelper.IsLocalPlayer(unit)) && autoBehavior.IsEnabled() == true && unit.IsIdle() == true) //if the auto behavior is, the unit is idle and this is the local player's faction or a single player game { if (autoBehavior.CanSearch() == true) //can the unit search for a target { OnTargetSearch(); } else { autoBehavior.UpdateTimer(); //update the timer. } } }
//a method that sends the unit to drop off resources at the drop off building public void SendToDropOff() { if (GameManager.MultiplayerGame == false) //if this is a singleplayer game then go ahead directly { SendToDropOffLocal(); } else if (RTSHelper.IsLocalPlayer(unit)) //multiplayer game and this is the collector's owner { NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.unit, targetMode = (byte)InputMode.dropoff, initialPosition = transform.position }; InputManager.SendInput(newInput, unit, dropOffBuilding); } }
//called when the building picks a target public override void SetTarget(FactionEntity newTarget, Vector3 newTargetPosition) { if (GameManager.MultiplayerGame == false) //single player game, go ahead { SetTargetLocal(newTarget, newTargetPosition); } else if (RTSHelper.IsLocalPlayer(building) == true) //only if this is a local player { NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.building, targetMode = (byte)InputMode.attack, targetPosition = newTargetPosition, }; InputManager.SendInput(newInput, building, newTarget); //send input return; } }
//a method called to eject all units public void EjectAll(bool destroyed) { if (GameManager.MultiplayerGame == false) //if this is a singleplayer game then go ahead directly { EjectAllLocal(destroyed); } else if (RTSHelper.IsLocalPlayer(FactionEntity)) //multiplayer game and this is the APC's owner { NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.APC, targetMode = (byte)InputMode.APCEjectAll, value = destroyed == true ? 1 : 0 }; InputManager.SendInput(newInput, FactionEntity, null); //send input to input manager } }
public void Init(GameManager gameMgr, Unit unit) { this.gameMgr = gameMgr; this.unit = unit; if (isActive == false || activeByDefault == false) //if the Wandering component is either not enabled or unit can't wander when created { return; //do not proceed } if (GameManager.MultiplayerGame == false || RTSHelper.IsLocalPlayer(unit)) //if this is a single player game or the local player owns this object { //set the wandering center if (fixedCenter == true) { center = transform.position; } Trigger(); } }
//a method that checks if the building is near a specific resource that it's supposed to built in range of public bool IsBuildingNearResource() { //if we can place the building free from being in range of a resource or this is not the player's faction (as this doesn't apply for NPC factions, they are managed differently). if (placeNearResource == false || RTSHelper.IsLocalPlayer(building) == false) { return(true); } foreach (Resource resource in gameMgr.ResourceMgr.GetAllResources()) //go through all resources { if (resource.GetResourceType() == resourceType) //make sure this is the resource that the building needs (matching names) { if (Vector3.Distance(resource.transform.position, transform.position) < resourceRange) //if the building is inside the correct range { return(true); //found requested resource } } } return(false); //failure in case no resource that matched the requested settings is found }
//Randomize the order of the factions inside the faction order: private void RandomizePlayerFaction() { //if it is allowed to randomize player faction. if (randomPlayerFaction == true) { //swap faction slots randomly. for (int i = 0; i < Factions.Count; i++) { //randomly a faction ID to swap with int swapID = Random.Range(0, Factions.Count); //if it's not the same faction: if (i != swapID) { //swap capital building & NPC Manager prefabs: RTSHelper.Swap <Building>(ref Factions[swapID].CapitalBuilding, ref Factions[i].CapitalBuilding); RTSHelper.Swap <NPCManager>(ref Factions[swapID].npcMgr, ref Factions[i].npcMgr); } } } }
//called when the unit attempts to set a new target public virtual ErrorMessage SetTarget(E newTarget, InputMode targetMode = InputMode.none) { if (GameManager.MultiplayerGame == false) //if this is a singleplayer game then go ahead directly { SetTargetLocal(newTarget); } else if (RTSHelper.IsLocalPlayer(unit)) //multiplayer game and this is the unit's owner { NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.unit, targetMode = (byte)targetMode, initialPosition = transform.position, targetPosition = newTarget.transform.position }; InputManager.SendInput(newInput, unit, newTarget); } return(ErrorMessage.none); }
//a method called to destroy the faction entity: public void DestroyFactionEntity(bool upgrade) { if (GameManager.MultiplayerGame == false) //if it's a single player game { DestroyFactionEntityLocal(upgrade); //destroy faction entity directly } else //multiplayer game { if (RTSHelper.IsLocalPlayer(factionEntity)) //make sure that it's this faction entity belongs to the local player { //send input action to the input manager NetworkInput NewInputAction = new NetworkInput { sourceMode = (byte)InputMode.destroy, targetMode = (byte)InputMode.factionEntity, value = (upgrade == true) ? 1 : 0 //when upgrade == true, then set to 1. if not set to 0 }; InputManager.SendInput(NewInputAction, factionEntity, null); //send to input manager } } }
//a method that stops the unit's movement. /// <summary> /// Stops the current unit's movement. /// </summary> /// <param name="prepareNextMovement">When true, not all movement settings will be reset since a new movement command will be followed. Default value: false.</param> public void Stop(bool prepareNextMovement = false) { DisablePendingMovement(true); AudioManager.Stop(unit.AudioSourceComp); //stop the movement audio from playing if (isMoving == false) //if the unit is not moving already then stop here { return; } isMoving = false; //marked as not moving SetMaxSpeed(speed); //set the movement speed to the default one in case it was changed by the Attack on Escape component. //unit doesn't have a target APC or Portal to move to anymore targetAPC = null; targetPortal = null; if (!resetPendingMovement && prepareNextMovement) //if we're preparing for another movement command that will follow this call here, then no need to reset some of the params { return; } if (!useNavAgent) //if we're using the NavAgent to move the unit, it will handle keeping the height of the unit in check { StopCoroutine(heightCheckCoroutine); } else { navAgent.isStopped = true; //using the NavAgent component? then stop updating the unit's position. } rotationTarget = RTSHelper.GetLookRotation(transform, lookAtTarget); //update the rotation target using the registered lookAt position. UpdateTargetPositionCollider(transform.position); //set the target position's collider to the current unit position since the movement has stopped. if (!unit.HealthComp.IsDead()) //if the unit is not dead { unit.SetAnimState(UnitAnimState.idle); //get into idle state } }
//add/remove health to the faction entity's: public void AddHealth(int value, FactionEntity source) { if (GameManager.MultiplayerGame == false) //if it's a single player game { AddHealthLocal(value, source); //add the health directly } else //multiplayer game { if (RTSHelper.IsLocalPlayer(factionEntity)) //make sure that it's this faction entity belongs to the local player { //crete new input NetworkInput newInput = new NetworkInput { sourceMode = (byte)InputMode.factionEntity, targetMode = (byte)InputMode.health, value = value }; //send the input to the input manager InputManager.SendInput(newInput, factionEntity, source); } } }
//the Move method is called when another component requests the movement of a single unit public void Move(Unit unit, Vector3 destination, float offsetRadius, Entity target, InputMode targetMode, bool playerCommand) { if (GameManager.MultiplayerGame == false) //single player game, directly prepare the unit's movement { PrepareMove(unit, destination, offsetRadius, target, targetMode, playerCommand); } else if (RTSHelper.IsLocalPlayer(unit)) //multiplayer game and this is the local player { //send input action to the input manager NetworkInput newInput = new NetworkInput() { sourceMode = (byte)InputMode.unit, targetMode = (byte)targetMode, initialPosition = unit.transform.position, targetPosition = destination, value = Mathf.FloorToInt(offsetRadius) }; //sent input InputManager.SendInput(newInput, unit, target); } }
//moving the unit along its computed path private void MoveAlongPath() { float currentDistance = (transform.position - currentDestination).sqrMagnitude; //compute the distance between the current unit's position and the next corner in the path //update the rotation as long as the unit is moving to look at the next corner in the path queue. transform.rotation = Quaternion.Slerp(transform.rotation, RTSHelper.GetLookRotation(transform, currentDestination), Time.deltaTime * rotationDamping); //if the unit can't move before it faces a certain angle towards its next destination in the path if (canMoveRotate == false && facingNextDestination == false) { //keep checking if the angle between the unit and its next destination Vector3 lookAt = currentDestination - transform.position; lookAt.y = 0.0f; //as long as the angle is still over the min allowed movement angle, then do not proceed to keep moving if (Vector3.Angle(transform.forward, lookAt) > minMoveAngle) { return; } else { facingNextDestination = true; } } //if this is the last corner or the player's distance to the next corner reaches a min value, move to the next corner, if not keep moving the player towards the current corner. if (currentDistance > stoppingDistance || navPathCornerQueue.Count == 0) { //acceleration: CurrentSpeed = CurrentSpeed >= maxSpeed ? maxSpeed : CurrentSpeed + acceleration * Time.deltaTime; //move the unit on the x and z axis using the assigned speed transform.position += new Vector3(currentDirection.x * CurrentSpeed * Time.deltaTime, 0.0f, currentDirection.z * CurrentSpeed * Time.deltaTime); } else { GetNextCorner(); } }
private void Update() { if (status != ScenarioStatus.active) //if the current scenario isn't active { return; } string nextDescription = currMissionDescription; if (currTimeCondition.survivalTimeEnabled) //if survival time is enabled for the current mission { if (currTimeCondition.survivalTime <= 0.0f) //survival time is done { scenario.GetMission(currMissionID).Complete(); //complete the mission since the player survived for the assigned time return; } //keep displaying the remaining survival time and run the timer as well nextDescription += "\nSurvival Time: " + RTSHelper.TimeToString(currTimeCondition.survivalTime); currTimeCondition.survivalTime -= Time.deltaTime; } if (currTimeCondition.timeLimitEnabled) //if time limit (time to finish the mission before it is forfeited) is enabled { //if the time limit is already over: if (currTimeCondition.timeLimit <= 0.0f) { OnFailed(); //scenario hasn't been successfully completed return; } //display the time limit and run the timer: nextDescription += "\nTime Left: " + RTSHelper.TimeToString(currTimeCondition.timeLimit); currTimeCondition.timeLimit -= Time.deltaTime; } //update the mission's description missionDescriptionText.text = nextDescription; }