Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs
T
Joonas Rikkonen a9d8c14b05 3ea33fb...bbc4a31
commit bbc4a31aa83572258226f303ab767f8546de64fb
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Tue Mar 5 13:08:38 2019 +0200

    Fixed missing item names in the extra cargo menu. Closes #1218

commit dd18bd163e05e8ba4718c0e98083e50ef0a0157e
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Tue Mar 5 13:04:54 2019 +0200

    Fixed a compiler error in RespawnManager

commit 904700eda3e4da5468208bd7553a803e9f41234e
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Tue Mar 5 12:56:29 2019 +0200

    Autorestart/spectating fixes (closes #1219):
    - The server owner and spectators don't trigger autorestart.
    - The owner is allowed to spectate even if spectating is disallowed in server settings.
    - Fixed "play yourself" always toggling to true when a round ends.

commit 9710612256875d5a788fb34371ca8ea6dd61b749
Author: ezjamsen <ezjames.fi@gmail.com>
Date:   Tue Mar 5 10:10:25 2019 +0200

    dropped the point at which damage sprites appear slightly.
2019-03-18 22:29:57 +02:00

256 lines
10 KiB
C#

using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
namespace Barotrauma
{
partial class HumanAIController : AIController
{
public static bool DisableCrewAI;
const float UpdateObjectiveInterval = 0.5f;
private AIObjectiveManager objectiveManager;
private float updateObjectiveTimer;
private bool shouldCrouch;
private float crouchRaycastTimer;
const float CrouchRaycastInterval = 1.0f;
private SteeringManager outsideSteering, insideSteering;
public override AIObjectiveManager ObjectiveManager
{
get { return objectiveManager; }
}
public Order CurrentOrder
{
get;
private set;
}
public string CurrentOrderOption
{
get;
private set;
}
public HumanAIController(Character c) : base(c)
{
insideSteering = new IndoorsSteeringManager(this, true, false);
outsideSteering = new SteeringManager(this);
objectiveManager = new AIObjectiveManager(c);
objectiveManager.AddObjective(new AIObjectiveFindSafety(c));
objectiveManager.AddObjective(new AIObjectiveIdle(c));
updateObjectiveTimer = Rand.Range(0.0f, UpdateObjectiveInterval);
InitProjSpecific();
}
partial void InitProjSpecific();
public override void Update(float deltaTime)
{
if (DisableCrewAI || Character.IsUnconscious) return;
if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null)
{
if (steeringManager != insideSteering) insideSteering.Reset();
steeringManager = insideSteering;
}
else
{
if (steeringManager != outsideSteering) outsideSteering.Reset();
steeringManager = outsideSteering;
}
(Character.AnimController as HumanoidAnimController).Crouching = shouldCrouch;
CheckCrouching(deltaTime);
Character.ClearInputs();
if (updateObjectiveTimer > 0.0f)
{
updateObjectiveTimer -= deltaTime;
}
else
{
objectiveManager.UpdateObjectives();
updateObjectiveTimer = UpdateObjectiveInterval;
}
if (Character.SpeechImpediment < 100.0f)
{
ReportProblems();
UpdateSpeaking();
}
objectiveManager.DoCurrentObjective(deltaTime);
float currObjectivePriority = objectiveManager.GetCurrentPriority();
bool run = currObjectivePriority > 30.0f;
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f &&
(-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));
var currPath = (steeringManager as IndoorsSteeringManager)?.CurrentPath;
if (currPath != null && currPath.CurrentNode != null)
{
if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y)
{
ignorePlatforms = true;
}
}
Character.AnimController.IgnorePlatforms = ignorePlatforms;
if (!Character.AnimController.InWater)
{
Vector2 targetMovement = new Vector2(
Character.AnimController.TargetMovement.X,
MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
float maxSpeed = Character.GetCurrentMaxSpeed(run);
targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed);
targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed);
//apply speed multiplier if
// a. it's boosting the movement speed and the character is trying to move fast (= running)
// b. it's a debuff that decreases movement speed
float speedMultiplier = Character.SpeedMultiplier;
if (run || speedMultiplier <= 0.0f) targetMovement *= speedMultiplier;
Character.ResetSpeedMultiplier(); // Reset, items will set the value before the next update
Character.AnimController.TargetMovement = targetMovement;
}
if (Character.AnimController.Anim == AnimController.Animation.Climbing &&
Character.SelectedConstruction != null &&
Character.SelectedConstruction.GetComponent<Items.Components.Ladder>() != null)
{
if (currPath != null && currPath.CurrentNode != null && currPath.CurrentNode.Ladders != null)
{
Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
}
}
//suit can be taken off if there character is inside a hull and there's air in the room
bool canTakeOffSuit = Character.AnimController.CurrentHull != null &&
Character.AnimController.CurrentHull.OxygenPercentage > 30.0f &&
Character.AnimController.CurrentHull.WaterVolume < Character.AnimController.CurrentHull.Volume * 0.3f;
//the suit can be taken off and the character is running out of oxygen (couldn't find a tank for the suit?) or idling
//-> take the suit off
if (canTakeOffSuit && (Character.Oxygen < 50.0f || objectiveManager.CurrentObjective is AIObjectiveIdle))
{
var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit");
if (divingSuit != null) divingSuit.Drop(Character);
}
if (Character.IsKeyDown(InputType.Aim))
{
var cursorDiffX = Character.CursorPosition.X - Character.Position.X;
if (cursorDiffX > 10.0f)
{
Character.AnimController.TargetDir = Direction.Right;
}
else if (cursorDiffX < -10.0f)
{
Character.AnimController.TargetDir = Direction.Left;
}
if (Character.SelectedConstruction != null) Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
}
else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
{
Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
}
}
private void ReportProblems()
{
if (GameMain.Client != null) return;
partial void ReportProblems();
private void UpdateSpeaking()
{
if (Character.Oxygen < 20.0f)
{
Character.Speak(TextManager.Get("DialogLowOxygen"), null, 0, "lowoxygen", 30.0f);
}
if (Character.Bleeding > 2.0f)
{
Character.Speak(TextManager.Get("DialogBleeding"), null, 0, "bleeding", 30.0f);
}
if (Character.PressureTimer > 50.0f && Character.CurrentHull != null)
{
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.RoomName), null, 0, "pressure", 30.0f);
}
}
public override void OnAttacked(Character attacker, AttackResult attackResult)
{
float totalDamage = attackResult.Damage;
if (totalDamage <= 0.0f || attacker == null) return;
if (attacker.AnimController.Anim == AnimController.Animation.CPR && attacker.SelectedCharacter == Character)
{
// Don't attack characters that damage you while doing cpr, because let's assume that they are helping you.
// Should not cancel any existing ai objectives (so that if the character attacked you and then helped, we still would want to retaliate).
return;
}
objectiveManager.AddObjective(new AIObjectiveCombat(Character, attacker), Rand.Range(0.5f, 1, Rand.RandSync.Unsynced), () =>
{
//the objective in the manager is not necessarily the same as the one we just instantiated,
//because the objective isn't added if there's already an identical objective in the manager
var combatObjective = objectiveManager.GetObjective<AIObjectiveCombat>();
combatObjective.MaxEnemyDamage = Math.Max(totalDamage, combatObjective.MaxEnemyDamage);
});
}
public void SetOrder(Order order, string option, Character orderGiver, bool speak = true)
{
CurrentOrderOption = option;
CurrentOrder = order;
objectiveManager.SetOrder(order, option, orderGiver);
if (speak && Character.SpeechImpediment < 100.0f) Character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f);
SetOrderProjSpecific(order);
}
partial void SetOrderProjSpecific(Order order);
public override void SelectTarget(AITarget target)
{
SelectedAiTarget = target;
}
private void CheckCrouching(float deltaTime)
{
crouchRaycastTimer -= deltaTime;
if (crouchRaycastTimer > 0.0f) return;
crouchRaycastTimer = CrouchRaycastInterval;
//start the raycast in front of the character in the direction it's heading to
Vector2 startPos = Character.SimPosition;
startPos.X += MathHelper.Clamp(Character.AnimController.TargetMovement.X, -1.0f, 1.0f);
//do a raycast upwards to find any walls
float minCeilingDist = Character.AnimController.Collider.height / 2 + Character.AnimController.Collider.radius + 0.1f;
shouldCrouch = Submarine.PickBody(startPos, startPos + Vector2.UnitY * minCeilingDist, null, Physics.CollisionWall) != null;
}
}
}