protected void FixedUpdate() { HandleAnimation(); if (!Alive() || !GameManager.Instance.GameStarted || !navMeshAgent.isOnNavMesh) { return; } if (InArea && InArea.Visible() && MovementAudio && !MovementAudio.isPlaying) { MovementAudio.Play(); } else if (InArea && !InArea.Visible() && MovementAudio && MovementAudio.isPlaying) { MovementAudio.Stop(); } if (SPE.GetStatMax() < 1) { Debug.LogWarning("Speed is 0"); } navMeshAgent.speed = Walking ? Mathf.Min(WalkingSpeed, SPE.GetStatMax()) / 2f : SPE.GetStatMax() / 2f; DesiredVelocity = navMeshAgent.desiredVelocity.sqrMagnitude; AgentVelocity = navMeshAgent.velocity.sqrMagnitude; HasPath = navMeshAgent.hasPath; PathStale = navMeshAgent.isPathStale; IsOnNavMesh = navMeshAgent.isOnNavMesh; //if (IncoherentNavAgentSpeed() && agentStuckRoutine == null) // agentStuckRoutine = StartCoroutine(CheckForNavAgentStuck(0.25f)); //TODO: merge together with move's switch statement if (Attacking() && AttackTarget && AttackTarget.Alive() && InAttackRange() ) //has live enemy target and in attackrange { navMeshAgent.isStopped = true; if (_attackRoutine == null) { _attackRoutine = StartCoroutine(AttackRoutine()); } } else { navMeshAgent.isStopped = false; SelectAction(); } }
private IEnumerator AttackRoutine() { //Debug.Log(gameObject.name + " is Attacking " + AttackTarget.gameObject.name); State = CharacterState.Attacking; while (Attacking() && InAttackRange() && AttackTarget.Alive()) { (this as Goblin)?.Speak(SoundBank.GoblinSound.Attacking); //HIT TARGET var damage = Random.Range(1, DMG.GetStatMax()) * OutgoingDmgPct; if (AttackTarget.Surprised()) { damage = (int)(damage * AmbushModifier); } var target = AttackTarget; if (!(target.Team && GameManager.Instance.InvincibleMode)) { target.Health -= (int)Mathf.Round(damage * target.IncomingDmgPct); } if (target.Health <= 0) { //Debug.Log(name + " killed " + target.name); if (this as Goblin) { ((Goblin)this).Xp += GameManager.XpKill(); if (Team) { Team.OnTeamKill.Invoke(); } (this as Goblin)?.Speak(SoundBank.GoblinSound.Laugh); } break; } //Debug.Log(gameObject.name + " hit " + AttackTarget.gameObject.name +" for " + Damage + " damage"); //should be tied to animation maybe? yield return(new WaitForSeconds(AttackTime)); } _attackRoutine = null; }
private bool InAttackRange() { if (!AttackTarget || !AttackTarget.Alive()) { return(false); } var targetCol = AttackTarget.GetComponent <CapsuleCollider>(); if (!targetCol) { Debug.LogWarning(name + "'s target does not have a capsule collider"); return(false); } var boxCol = GetComponent <CapsuleCollider>(); return((boxCol.transform.position - (targetCol.transform.position)).magnitude <= boxCol.radius * boxCol.transform.lossyScale.x + targetCol.radius * targetCol.transform.lossyScale.x + AttackRange); }
private void SelectAction() { switch (State) { case CharacterState.Idling: //reset morale Morale = COU.GetStatMax() * 2; if (actionInProgress) { if (navMeshAgent.remainingDistance < 0.02f) { actionInProgress = false; } } else if (IrritationMeter >= IrritaionTolerance) { ChangeState(CharacterState.Attacking); } else if (Random.value < 0.015f) //selecting idle action { (this as Goblin)?.Speak(PlayerController.GetDynamicReactions(PlayerController.DynamicState.Idle)); actionInProgress = true; Vector3 dest; if (InArea) { if (GetClosestEnemy() && ( //ANY friends fighting InArea.PresentCharacters.Any(c => c.tag == tag && c.Alive() && c.Attacking()) // I am aggressive wanderer || (StickToRoad && InArea.PresentCharacters.Any(c => c.tag == "Player" & !c.Hiding())) )) { //Debug.Log(name + ": Joining attack without beeing attacked"); ChangeState(CharacterState.Attacking, true); Morale -= 5; Target = GetClosestEnemy().transform.position; dest = Target; } else if ((this as Goblin) && Team && Team.Leader.InArea != InArea && Team.Leader.Idling()) { dest = Team.Leader.InArea.GetRandomPosInArea(); } else if ((this as Goblin) && tag == "Player" && GetClosestEnemy() && (GetClosestEnemy().transform.position - transform.position).magnitude < provokeDistance) { ChangeState(CharacterState.Provoking, true); var ctx = GetClosestEnemy(); (this as Goblin).ProvokeTarget = ctx; (this as Goblin)?.Speak(PlayerController.GetDynamicReactions(PlayerController.DynamicState.Mocking)); dest = ctx.transform.position; } else if (StickToRoad) { var goingTo = InArea.GetClosestNeighbour(transform.position, true); dest = goingTo.PointOfInterest ? goingTo.GetRandomPosInArea(): goingTo.transform.position; //Debug.Log(name + ": Wandering to "+ goingTo); Target = dest; goingTo.PresentCharacters.ForEach(c => StartCoroutine(c.SpotArrivalCheck(this))); ChangeState(CharacterState.Travelling, true); } else { dest = InArea.GetRandomPosInArea(); } } else { dest = transform.position + Random.insideUnitSphere * idleDistance; dest.y = 0; } navMeshAgent.SetDestination(dest); //new Vector3(Random.Range(-idleDistance, idleDistance), 0,Random.Range(-idleDistance, idleDistance))); } //TODO: use a different method for activity selection than else if else if (Random.value < 0.0025f && this as Goblin && Team && !(Team.Leader == this) && (this as Goblin).ClassType > Goblin.Class.Slave & !Team.Challenger && (Team.Leader as Goblin).CurrentLevel < ((Goblin)this).CurrentLevel) { //TODO: make it only appear after a while Debug.Log("Chief Fight!!"); Team.ChallengeForLeadership(this as Goblin); } //TODO: define better which characters should search stuff else if (Random.value < 0.001f * SMA.GetStatMax() && Team && this as Goblin && !InArea.AnyEnemies() && InArea.Lootables.Any(l => !l.Searched)) { var loots = InArea.Lootables.Where(l => !l.Searched).ToArray(); var loot = loots[Random.Range(0, loots.Count())]; (this as Goblin).Search(loot); } break; case CharacterState.Attacking: if (AttackTarget && AttackTarget.Alive() && AttackTarget.InArea == InArea) { if (AttackTarget.Fleeing()) { var c = GetClosestEnemy(); if (c) { AttackTarget = c; } } navMeshAgent.SetDestination(AttackTarget.transform.position); //TODO: add random factor } else { TargetGone(); } break; case CharacterState.Travelling: navMeshAgent.SetDestination(Target); //check for arrival and stop travelling if (Vector3.Distance(transform.position, Target) < 3f) { //Debug.Log(name +" arrived at target"); State = CharacterState.Idling; actionInProgress = false; break; } break; case CharacterState.Fleeing: (this as Goblin)?.Speak(SoundBank.GoblinSound.PanicScream); //if (actionInProgress &! navMeshAgent.hasPath) //{ // //TODO: move into next if statement, if correct // Debug.Log("stuck fleeing resolved"); // actionInProgress = false; // ChangeState(CharacterState.Idling, true); //} if (fleeingToArea == InArea && navMeshAgent.remainingDistance < 0.1f) { actionInProgress = false; ChangeState(CharacterState.Idling, true); } else if (!actionInProgress) { fleeingToArea = InArea.GetClosestNeighbour(transform.position, StickToRoad); navMeshAgent.SetDestination(fleeingToArea.GetRandomPosInArea()); actionInProgress = true; } break; case CharacterState.Dead: break; case CharacterState.Hiding: if (!Hidingplace) { State = CharacterState.Idling; } //already set the destination if ((navMeshAgent.destination - hiding.HideLocation.transform.position).sqrMagnitude < 1f) { if (InArea.AnyEnemies() && navMeshAgent.remainingDistance > 0.2f) { ChangeState(CharacterState.Idling); } } else { navMeshAgent.SetDestination(hiding.HideLocation.transform.position); } break; //Only to be used for chief fights. TODO: rename case CharacterState.Watching: if (!Team || !Team.Challenger) { //cheer (this as Goblin)?.Speak(SoundBank.GoblinSound.Laugh); ChangeState(CharacterState.Idling, true); } else { if (Vector3.Distance(transform.position, Team.Challenger.transform.position) < 3f && Team.Challenger != this && Team.Leader != this) { //cheer (this as Goblin)?.Speak(PlayerController.GetDynamicReactions(PlayerController.DynamicState.ChiefBattleCheer)); navMeshAgent.ResetPath(); } else if (!actionInProgress) { navMeshAgent.SetDestination(Team.Challenger.transform.position); actionInProgress = true; } } break; case CharacterState.Searching: //check for arrival and stop travelling if (Vector3.Distance(transform.position, LootTarget.transform.position) < 2f) { if (LootTarget.ContainsLoot) { (this as Goblin)?.Speak(SoundBank.GoblinSound.Laugh); PopUpText.ShowText(name + " found " + LootTarget.Loot); Team.OnTreasureFound.Invoke(1); } if (LootTarget.ContainsFood) { (this as Goblin)?.Speak(SoundBank.GoblinSound.Laugh); PopUpText.ShowText(name + " found " + LootTarget.Food); Team.OnFoodFound.Invoke(5); } if (this as Goblin) { foreach (var equipment in LootTarget.EquipmentLoot) { //TODO: create player choice for selecting goblin (this as Goblin)?.Speak(SoundBank.GoblinSound.Laugh); PopUpText.ShowText(name + " found " + equipment.name); if (Team && Team.Members.Count > 1) { Team.OnEquipmentFound.Invoke(equipment, this as Goblin); } else { Equip(equipment); } } } LootTarget.EquipmentLoot.Clear(); LootTarget.ContainsFood = false; LootTarget.ContainsLoot = false; LootTarget.Searched = true; State = CharacterState.Idling; break; } break; case CharacterState.Provoking: var g = this as Goblin; if (!g) { Debug.LogWarning("Non-goblin is being provocative"); break; } if (!g.ProvokeTarget || g.ProvokeTarget.InArea != InArea) { (this as Goblin)?.Speak(SoundBank.GoblinSound.Laugh); g.ProvokeTarget = GetClosestEnemy(); } if (!g.ProvokeTarget) { ChangeState(CharacterState.Idling, true); break; } if (g.ProvokeTarget.Attacking()) { ChangeState(CharacterState.Attacking); break; } if (!actionInProgress && navMeshAgent.remainingDistance < 0.5f) { actionInProgress = true; navMeshAgent.SetDestination(g.ProvokeTarget.transform.position); ProvokeStartTime = Time.time; break; } if (ProvokeTime + ProvokeStartTime > Time.time) { //run away actionInProgress = false; var dest = InArea.GetRandomPosInArea(); navMeshAgent.SetDestination(dest); g.ProvokeTarget.IrritationMeter++; break; } if ((g.ProvokeTarget.transform.position - transform.position).magnitude < 4) //Provoke { navMeshAgent.isStopped = true; if (Random.value < 0.2f) { (this as Goblin)?.Speak( PlayerController.GetDynamicReactions(PlayerController.DynamicState.Mocking)); } } break; case CharacterState.Surprised: navMeshAgent.isStopped = true; if (SurprisedTime + SurprisedStartTime <= Time.time) { ChangeState(CharacterState.Attacking); } break; case CharacterState.Resting: if (!Team || !Team.Campfire) { ChangeState(CharacterState.Idling, true); } else { if (Vector3.Distance(transform.position, Team.Campfire.transform.position) < 4f) { (this as Goblin)?.Speak(SoundBank.GoblinSound.Eat); navMeshAgent.ResetPath(); } else if (!actionInProgress) { navMeshAgent.SetDestination(Team.Campfire.transform.position); actionInProgress = true; } } break; default: break; } }