v0.14.6.0
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
using Barotrauma.Extensions;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class MentalStateManager
|
||||
{
|
||||
private float mentalStateTimer;
|
||||
private const float MentalStateInterval = 7.5f;
|
||||
|
||||
private float mentalBehaviorTimer;
|
||||
private const float MentalBehaviorInterval = 7.5f;
|
||||
|
||||
private readonly Character character;
|
||||
private readonly HumanAIController humanAIController;
|
||||
|
||||
public bool Active { get; set; }
|
||||
public MentalType CurrentMentalType { get; private set; }
|
||||
public enum MentalType
|
||||
{
|
||||
Normal,
|
||||
Confused, // No effects other than special dialogue
|
||||
Afraid, // Will retreat from whoever is nearby
|
||||
Desperate, // Will defensively attack/arrest whoever is nearby
|
||||
Berserk // turns fully hostile using team change logic
|
||||
}
|
||||
|
||||
private const string MentalTeamChange = "mental";
|
||||
|
||||
public MentalStateManager(Character character, HumanAIController humanAIController)
|
||||
{
|
||||
this.character = character;
|
||||
this.humanAIController = humanAIController;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (!Active) { return; }
|
||||
mentalStateTimer -= deltaTime;
|
||||
if (mentalStateTimer <= 0.0f)
|
||||
{
|
||||
UpdateMentalState();
|
||||
mentalStateTimer = MentalStateInterval * Rand.Range(0.75f, 1.25f);
|
||||
}
|
||||
|
||||
mentalBehaviorTimer = Math.Max(0f, mentalBehaviorTimer - deltaTime);
|
||||
}
|
||||
|
||||
private void UpdateMentalState()
|
||||
{
|
||||
MentalType newMentalType = GetMentalType(character.CharacterHealth.GetAffliction("psychosis"));
|
||||
bool createdCombat = false;
|
||||
|
||||
switch (newMentalType)
|
||||
{
|
||||
case MentalType.Normal:
|
||||
case MentalType.Confused:
|
||||
// remove combat if we became normal again
|
||||
mentalBehaviorTimer = 0f;
|
||||
break;
|
||||
case MentalType.Afraid:
|
||||
case MentalType.Desperate:
|
||||
case MentalType.Berserk:
|
||||
// berserk is not removed unless we drop to normal behavior again
|
||||
if (CurrentMentalType == MentalType.Berserk)
|
||||
{
|
||||
newMentalType = MentalType.Berserk;
|
||||
}
|
||||
// give players a full interval to react to mental changes
|
||||
if (newMentalType == CurrentMentalType)
|
||||
{
|
||||
createdCombat = CreateCombatBehavior(CurrentMentalType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!createdCombat)
|
||||
{
|
||||
CreateDialogueBehavior(newMentalType);
|
||||
}
|
||||
|
||||
if (newMentalType != MentalType.Berserk)
|
||||
{
|
||||
character.TryRemoveTeamChange(MentalTeamChange);
|
||||
}
|
||||
|
||||
CurrentMentalType = newMentalType;
|
||||
}
|
||||
|
||||
private int mentalTypeCount;
|
||||
private int MentalTypeCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mentalTypeCount == 0)
|
||||
{
|
||||
mentalTypeCount = Enum.GetNames(typeof(MentalType)).Length;
|
||||
}
|
||||
return mentalTypeCount;
|
||||
}
|
||||
}
|
||||
|
||||
private MentalType GetMentalType(Affliction affliction)
|
||||
{
|
||||
if (affliction == null)
|
||||
{
|
||||
return MentalType.Normal;
|
||||
}
|
||||
// test this later
|
||||
int psychosisIndex = (int)(affliction.Strength / (affliction.Prefab.MaxStrength / MentalTypeCount) * Rand.Range(1f, 1.2f));
|
||||
psychosisIndex = Math.Clamp(psychosisIndex, 0, 4);
|
||||
MentalType mentalType = psychosisIndex switch
|
||||
{
|
||||
0 => MentalType.Normal,
|
||||
1 => MentalType.Confused,
|
||||
2 => MentalType.Afraid,
|
||||
3 => MentalType.Desperate,
|
||||
4 => MentalType.Berserk,
|
||||
_ => throw new ArgumentOutOfRangeException(psychosisIndex.ToString()),
|
||||
};
|
||||
return mentalType;
|
||||
}
|
||||
|
||||
public bool CreateCombatBehavior(MentalType mentalType)
|
||||
{
|
||||
Character mentalAttackTarget = Character.CharacterList.Where(
|
||||
possibleTarget => HumanAIController.IsActive(possibleTarget) &&
|
||||
(possibleTarget.TeamID != character.TeamID || mentalType == MentalType.Berserk) &&
|
||||
humanAIController.VisibleHulls.Contains(possibleTarget.CurrentHull) &&
|
||||
possibleTarget != character).GetRandom();
|
||||
|
||||
if (mentalAttackTarget == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var combatMode = AIObjectiveCombat.CombatMode.None;
|
||||
bool holdFire = mentalType == MentalType.Afraid && character.IsSecurity;
|
||||
switch (mentalType)
|
||||
{
|
||||
case MentalType.Afraid:
|
||||
combatMode = character.IsSecurity ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Retreat;
|
||||
break;
|
||||
case MentalType.Desperate:
|
||||
// might be unnecessary to explicitly declare as arrest against non-humans
|
||||
combatMode = character.IsSecurity && mentalAttackTarget.IsHuman ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Defensive;
|
||||
break;
|
||||
case MentalType.Berserk:
|
||||
combatMode = AIObjectiveCombat.CombatMode.Offensive;
|
||||
break;
|
||||
}
|
||||
|
||||
// using this as an explicit time-out for the behavior. it's possible it will never run out because of the manager being disabled, but combat objective has failsafes for that
|
||||
mentalBehaviorTimer = MentalBehaviorInterval;
|
||||
humanAIController.AddCombatObjective(combatMode, mentalAttackTarget, allowHoldFire: holdFire, abortCondition: obj => mentalBehaviorTimer <= 0f);
|
||||
string textIdentifier = $"dialogmentalstatereaction{combatMode.ToString().ToLowerInvariant()}";
|
||||
character.Speak(TextManager.Get(textIdentifier), delay: Rand.Range(0.5f, 1.0f), identifier: textIdentifier, minDurationBetweenSimilar: 25f);
|
||||
|
||||
if (mentalType == MentalType.Berserk && !character.HasTeamChange(MentalTeamChange))
|
||||
{
|
||||
// TODO: could this be handled in the switch block above?
|
||||
character.TryAddNewTeamChange(MentalTeamChange, new ActiveTeamChange(CharacterTeamType.None, ActiveTeamChange.TeamChangePriorities.Absolute, aggressiveBehavior: true));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CreateDialogueBehavior(MentalType mentalType)
|
||||
{
|
||||
if (mentalType == MentalType.Normal) { return; }
|
||||
string textIdentifier = $"dialogmentalstate{mentalType.ToString().ToLowerInvariant()}";
|
||||
character.Speak(TextManager.Get(textIdentifier), delay: Rand.Range(0.5f, 1.0f), identifier: textIdentifier, minDurationBetweenSimilar: 35f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user