private void Move() { // Cancel movement and animations if paralyzed, but still allow gavity to take effect // This will have the (intentional for now) side-effect of making paralyzed flying enemies fall out of the air // Paralyzed swimming enemies will just freeze in place // Freezing anims also prevents the attack from triggering until paralysis cleared if (entityBehaviour.Entity.IsParalyzed) { mobile.FreezeAnims = true; if (swims) { controller.Move(Vector3.zero); } else { controller.SimpleMove(Vector3.zero); } return; } else { mobile.FreezeAnims = false; } // If hit, get knocked back if (knockBackSpeed > 0) { // Limit knockBackSpeed. This can be higher than what is actually used for the speed of motion, // making it last longer and do more damage if the enemy collides with something. if (knockBackSpeed > (40 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10))) { knockBackSpeed = (40 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10)); } if (knockBackSpeed > (5 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10)) && mobile.Summary.EnemyState != MobileStates.PrimaryAttack) { mobile.ChangeEnemyState(MobileStates.Hurt); } // Actual speed of motion is limited Vector3 motion; if (knockBackSpeed <= (25 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10))) { motion = knockBackDirection * knockBackSpeed; } else { motion = knockBackDirection * (25 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10)); } if (swims) { WaterMove(motion); } else if (flies || isLevitating) { controller.Move(motion * Time.deltaTime); } else { controller.SimpleMove(motion); } if (classicUpdate) { knockBackSpeed -= (5 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10)); if (knockBackSpeed <= (5 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10)) && mobile.Summary.EnemyState != MobileStates.PrimaryAttack) { mobile.ChangeEnemyState(MobileStates.Move); } } return; } // Monster speed of movement follows the same formula as for when the player walks EnemyEntity entity = entityBehaviour.Entity as EnemyEntity; float moveSpeed = ((entity.Stats.LiveSpeed + PlayerSpeedChanger.dfWalkBase) / PlayerSpeedChanger.classicToUnitySpeedUnitRatio); // Reduced speed if playing a one-shot animation if (mobile.IsPlayingOneShot()) { moveSpeed /= AttackSpeedDivisor; } // Remain idle when not hostile if (!isHostile) { mobile.ChangeEnemyState(MobileStates.Idle); return; } // If hostile but the enemy doesn't see the player, run the stealth check else if (senses.LastKnownPlayerPos == EnemySenses.ResetPlayerPos) { if (senses.StealthCheck()) { // Enemy noticed the player senses.LastKnownPlayerPos = GameManager.Instance.PlayerObject.transform.position; senses.DetectedPlayer = true; } else { // Enemy didn't notice the player mobile.ChangeEnemyState(MobileStates.Idle); senses.DetectedPlayer = false; return; } } // As long as the player is directly seen/heard, // giveUpTimer is reset to full if (senses.PlayerInSight || senses.PlayerInEarshot) { giveUpTimer = 200; } else if (giveUpTimer == 0) { // Player lost for too long, or wasn't in sight/earshot to begin with. Time to give up senses.LastKnownPlayerPos = EnemySenses.ResetPlayerPos; return; } // GiveUpTimer value is from classic, so decrease at the speed of classic's update loop if (!senses.PlayerInSight && !senses.PlayerInEarshot && giveUpTimer > 0 && classicUpdate) { giveUpTimer--; } // Enemy will keep moving towards last known player position targetPos = senses.LastKnownPlayerPos; // Flying enemies and slaughterfish aim for player face if (flies || isLevitating || (swims && mobile.Summary.Enemy.ID == (int)MonsterCareers.Slaughterfish)) { targetPos.y += 0.9f; } else { // Ground enemies target at their own height // This avoids short enemies from stepping on each other as they approach the player // Otherwise, their target vector aims up towards the player var playerController = GameManager.Instance.PlayerController; var deltaHeight = (playerController.height - controller.height) / 2; targetPos.y -= deltaHeight; } // Get direction & distance. var direction = targetPos - transform.position; float distance = direction.magnitude; // If attacking, randomly follow player with attack. if (mobile.Summary.EnemyState == MobileStates.PrimaryAttack) { if (!isAttackFollowsPlayerSet) { attackFollowsPlayer = (Random.Range(0f, 1f) > 0.5f); isAttackFollowsPlayerSet = true; } } else { isAttackFollowsPlayerSet = false; } if (attackFollowsPlayer) { transform.forward = direction.normalized; } // Bow attack for enemies that have the appropriate animation if (senses.PlayerInSight && 360 * MeshReader.GlobalScale < distance && distance < 2048 * MeshReader.GlobalScale) { if (senses.TargetIsWithinYawAngle(22.5f)) { if (mobile.Summary.Enemy.HasRangedAttack1 && mobile.Summary.Enemy.ID > 129 && mobile.Summary.Enemy.ID != 132) { // Random chance to shoot bow if (classicUpdate && DFRandom.rand() < 1000) { if (mobile.Summary.Enemy.HasRangedAttack1 && !mobile.Summary.Enemy.HasRangedAttack2 && mobile.Summary.EnemyState != MobileStates.RangedAttack1) { mobile.ChangeEnemyState(MobileStates.RangedAttack1); } else if (mobile.Summary.Enemy.HasRangedAttack2 && mobile.Summary.EnemyState != MobileStates.RangedAttack2) { mobile.ChangeEnemyState(MobileStates.RangedAttack2); } } // Otherwise hold ground else if (!mobile.IsPlayingOneShot()) { mobile.ChangeEnemyState(MobileStates.Idle); } } //else if (spellPoints > 0 && canCastRangeSpells && DFRandom.rand() % 40 == 0) TODO: Ranged spell shooting // CastRangedSpell(); // Spell Cast Animation; else { // If no ranged attack, move towards target PursueTarget(direction, moveSpeed); } } else { if (!mobile.IsPlayingOneShot()) { mobile.ChangeEnemyState(MobileStates.Move); } TurnToTarget(direction.normalized); return; } } // Move towards target else if (distance > stopDistance) { PursueTarget(direction, moveSpeed); } else if (!senses.TargetIsWithinYawAngle(22.5f)) { TurnToTarget(direction.normalized); } //else //{ // TODO: Touch spells. //if (hasSpellPoints && attackCoolDownFinished && CanCastTouchSpells) //{ // Cast Touch Spell // Spell Cast Animation //} //} else if (!senses.PlayerInSight && !senses.PlayerInEarshot) { mobile.ChangeEnemyState(MobileStates.Idle); } }
private void Move() { // If hit, get knocked back if (knockBackSpeed > 0) { // Limit knockBackSpeed. This can be higher than what is actually used for the speed of motion, // making it last longer and do more damage if the enemy collides with something. if (knockBackSpeed > (40 / (PlayerMotor.classicToUnitySpeedUnitRatio / 10))) { knockBackSpeed = (40 / (PlayerMotor.classicToUnitySpeedUnitRatio / 10)); } if (knockBackSpeed > (5 / (PlayerMotor.classicToUnitySpeedUnitRatio / 10)) && mobile.Summary.EnemyState != MobileStates.PrimaryAttack) { mobile.ChangeEnemyState(MobileStates.Hurt); } // Actual speed of motion is limited Vector3 motion; if (knockBackSpeed <= (25 / (PlayerMotor.classicToUnitySpeedUnitRatio / 10))) { motion = knockBackDirection * knockBackSpeed; } else { motion = knockBackDirection * (25 / (PlayerMotor.classicToUnitySpeedUnitRatio / 10)); } if (flies) { controller.Move(motion * Time.deltaTime); } else { controller.SimpleMove(motion); } if (classicUpdate) { knockBackSpeed -= (5 / (PlayerMotor.classicToUnitySpeedUnitRatio / 10)); if (knockBackSpeed <= (5 / (PlayerMotor.classicToUnitySpeedUnitRatio / 10)) && mobile.Summary.EnemyState != MobileStates.PrimaryAttack) { mobile.ChangeEnemyState(MobileStates.Move); } } return; } // Monster speed of movement follows the same formula as for when the player walks EnemyEntity entity = entityBehaviour.Entity as EnemyEntity; float moveSpeed = ((entity.Stats.LiveSpeed + PlayerMotor.dfWalkBase) / PlayerMotor.classicToUnitySpeedUnitRatio); // Reduced speed if playing a one-shot animation if (mobile.IsPlayingOneShot()) { moveSpeed /= AttackSpeedDivisor; } // Remain idle when not hostile if (!isHostile) { mobile.ChangeEnemyState(MobileStates.Idle); return; } // If hostile but the enemy doesn't see the player, run the stealth check else if (senses.LastKnownPlayerPos == EnemySenses.ResetPlayerPos) { if (senses.StealthCheck()) { // Enemy noticed the player senses.LastKnownPlayerPos = GameManager.Instance.PlayerObject.transform.position; senses.DetectedPlayer = true; } else { // Enemy didn't notice the player mobile.ChangeEnemyState(MobileStates.Idle); senses.DetectedPlayer = false; return; } } // As long as the player is directly seen/heard, // giveUpTimer is reset to full if (senses.PlayerInSight || senses.PlayerInEarshot) { giveUpTimer = 200; } else if (giveUpTimer == 0) { // Player lost for too long, or wasn't in sight/earshot to begin with. Time to give up senses.LastKnownPlayerPos = EnemySenses.ResetPlayerPos; return; } // GiveUpTimer value is from classic, so decrease at the speed of classic's update loop if (!senses.PlayerInSight && !senses.PlayerInEarshot && giveUpTimer > 0 && classicUpdate) { giveUpTimer--; } // Enemy will keep moving towards last known player position targetPos = senses.LastKnownPlayerPos; // Flying enemies aim for player face if (flies) { targetPos.y += 0.9f; } else { // Ground enemies target at their own height // This avoids short enemies from stepping on each other as they approach the player // Otherwise, their target vector aims up towards the player var playerController = GameManager.Instance.PlayerController; var deltaHeight = (playerController.height - controller.height) / 2; targetPos.y -= deltaHeight; } // Get direction & distance and face target. // If attacking, randomly do not do so so player has a chance to see // attack animations other than those directly facing the player var direction = targetPos - transform.position; float distance = direction.magnitude; if (mobile.IsPlayingOneShot() && !isAttackFollowsPlayerSet) { attackFollowsPlayer = (Random.Range(0f, 1f) > 0.5f); isAttackFollowsPlayerSet = true; } else if (!mobile.IsPlayingOneShot()) { isAttackFollowsPlayerSet = false; } if (!mobile.IsPlayingOneShot() || !attackFollowsPlayer) { transform.forward = direction.normalized; } // Bow attack for enemies that have the appropriate animation if (senses.PlayerInSight && 360 * MeshReader.GlobalScale < distance && distance < 2048 * MeshReader.GlobalScale && mobile.Summary.Enemy.HasRangedAttack1 && mobile.Summary.Enemy.ID > 129 && mobile.Summary.Enemy.ID != 132) { if (classicUpdate) { // Random chance to shoot bow if (DFRandom.rand() < 1000) { if (mobile.Summary.Enemy.HasRangedAttack1 && !mobile.Summary.Enemy.HasRangedAttack2 && mobile.Summary.EnemyState != MobileStates.RangedAttack1) { mobile.ChangeEnemyState(MobileStates.RangedAttack1); } else if (mobile.Summary.Enemy.HasRangedAttack2 && mobile.Summary.EnemyState != MobileStates.RangedAttack2) { mobile.ChangeEnemyState(MobileStates.RangedAttack2); } } // Otherwise hold ground else if (!mobile.IsPlayingOneShot()) { mobile.ChangeEnemyState(MobileStates.Idle); } } } // Move towards target else if (distance > stopDistance) { if (!mobile.IsPlayingOneShot()) { mobile.ChangeEnemyState(MobileStates.Move); } var motion = transform.forward * moveSpeed; // Prevent rat stacks (enemies don't stand on shorter enemies) AvoidEnemies(ref motion); if (flies) { controller.Move(motion * Time.deltaTime); } else { controller.SimpleMove(motion); } } else { // We have reached target, is player nearby? if (!senses.PlayerInSight && !senses.PlayerInEarshot) { mobile.ChangeEnemyState(MobileStates.Idle); senses.LastKnownPlayerPos = EnemySenses.ResetPlayerPos; } } }