Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/Source/Events/EventManager.cs
Joonas Rikkonen 23687fbf2f aeafa16...4d3cf73
2019-03-18 22:57:05 +02:00

325 lines
13 KiB
C#

using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class EventManager
{
const float IntensityUpdateInterval = 5.0f;
private List<ScriptedEvent> events;
private Level level;
//The "intensity" of the current situation (a value between 0.0 - 1.0).
//High when a disaster has struck, low when nothing special is going on.
private float currentIntensity;
//The exact intensity of the current situation, current intensity is lerped towards this value
private float targetIntensity;
//How low the intensity has to be for an event to be triggered.
//Gradually increases with time, so additional problems can still appear eventually even if
//the sub is laying broken on the ocean floor or if the players are trying to abuse the system
//by intentionally keeping the intensity high by causing breaches, damaging themselves or such
private float eventThreshold = 0.2f;
//New events can't be triggered when the cooldown is active.
private float eventCoolDown;
private float intensityUpdateTimer;
private float avgCrewHealth, avgHullIntegrity, floodingAmount, fireAmount, enemyDanger;
private float roundDuration;
private List<ScriptedEventSet> selectedEventSets;
private EventManagerSettings settings;
public float CurrentIntensity
{
get { return currentIntensity; }
}
public List<ScriptedEvent> Events
{
get { return events; }
}
public EventManager(GameSession session)
{
events = new List<ScriptedEvent>();
selectedEventSets = new List<ScriptedEventSet>();
}
public bool Enabled = true;
public void StartRound(Level level)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
var suitableSettings = EventManagerSettings.List.FindAll(s =>
level.Difficulty >= s.MinLevelDifficulty &&
level.Difficulty <= s.MaxLevelDifficulty);
if (suitableSettings.Count == 0)
{
DebugConsole.ThrowError("No suitable event manager settings found for the selected level (difficulty " + level.Difficulty + ")");
settings = EventManagerSettings.List[Rand.Int(EventManagerSettings.List.Count, Rand.RandSync.Server)];
}
else
{
settings = suitableSettings[Rand.Int(suitableSettings.Count, Rand.RandSync.Server)];
}
this.level = level;
var initialEventSet = SelectRandomEvents(ScriptedEventSet.List);
if (initialEventSet != null) selectedEventSets.Add(initialEventSet);
/*CreateInitialEvents();
foreach (ScriptedEvent ev in events)
{
ev.Init(false);
}*/
roundDuration = 0.0f;
intensityUpdateTimer = 0.0f;
CalculateCurrentIntensity(0.0f);
currentIntensity = targetIntensity;
eventThreshold = settings.DefaultEventThreshold;
eventCoolDown = 0.0f;
}
public void EndRound()
{
selectedEventSets.Clear();
events.Clear();
}
private ScriptedEventSet SelectRandomEvents(List<ScriptedEventSet> eventSets)
{
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
var allowedEventSets =
eventSets.Where(es => level.Difficulty >= es.MinLevelDifficulty && level.Difficulty <= es.MaxLevelDifficulty);
float totalCommonness = allowedEventSets.Sum(e => e.GetCommonness(level));
float randomNumber = (float)rand.NextDouble() * totalCommonness;
foreach (ScriptedEventSet eventSet in allowedEventSets)
{
float commonness = eventSet.GetCommonness(level);
if (randomNumber <= commonness)
{
return eventSet;
}
randomNumber -= commonness;
}
return null;
}
private void CreateEvents()
{
//don't create new events if docked to the start oupost
if (Level.Loaded?.StartOutpost != null &&
Submarine.MainSub.DockedTo.Contains(Level.Loaded.StartOutpost))
{
return;
}
for (int i = selectedEventSets.Count - 1; i >= 0; i--)
{
ScriptedEventSet eventSet = selectedEventSets[i];
float distFromStart = Vector2.Distance(Submarine.MainSub.WorldPosition, level.StartPosition);
float distFromEnd = Vector2.Distance(Submarine.MainSub.WorldPosition, level.EndPosition);
float distanceTraveled = MathHelper.Clamp(
(Submarine.MainSub.WorldPosition.X - level.StartPosition.X) / (level.EndPosition.X - level.StartPosition.X),
0.0f, 1.0f);
//don't create new events if within 50 meters of the start/end of the level
if (distanceTraveled <= 0.0f ||
distFromStart * Physics.DisplayToRealWorldRatio < 50.0f ||
distFromEnd * Physics.DisplayToRealWorldRatio < 50.0f)
{
continue;
}
if ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) &&
roundDuration < eventSet.MinMissionTime)
{
continue;
}
if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity)
{
continue;
}
selectedEventSets.RemoveAt(i);
if (eventSet.ChooseRandom)
{
if (eventSet.EventPrefabs.Count > 0)
{
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
var newEvent = eventSet.EventPrefabs[rand.NextInt32() % eventSet.EventPrefabs.Count].CreateInstance();
newEvent.Init(true);
DebugConsole.Log("Initialized event " + newEvent.ToString());
events.Add(newEvent);
}
if (eventSet.ChildSets.Count > 0)
{
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
var newEventSet = SelectRandomEvents(eventSet.ChildSets);
if (newEventSet != null) selectedEventSets.Add(newEventSet);
}
}
else
{
foreach (ScriptedEventPrefab eventPrefab in eventSet.EventPrefabs)
{
var newEvent = eventPrefab.CreateInstance();
newEvent.Init(true);
DebugConsole.Log("Initialized event " + newEvent.ToString());
events.Add(newEvent);
}
selectedEventSets.AddRange(eventSet.ChildSets);
}
}
}
public void Update(float deltaTime)
{
if (!Enabled) return;
//clients only calculate the intensity but don't create any events
//(the intensity is used for controlling the background music)
CalculateCurrentIntensity(deltaTime);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
roundDuration += deltaTime;
eventThreshold += settings.EventThresholdIncrease * deltaTime;
if (eventCoolDown > 0.0f)
{
eventCoolDown -= deltaTime;
}
else if (currentIntensity < eventThreshold)
{
CreateEvents();
eventThreshold = settings.DefaultEventThreshold;
eventCoolDown = settings.EventCooldown;
}
foreach (ScriptedEvent ev in events)
{
if (!ev.IsFinished)
{
ev.Update(deltaTime);
}
}
}
private void CalculateCurrentIntensity(float deltaTime)
{
intensityUpdateTimer -= deltaTime;
if (intensityUpdateTimer > 0.0f) return;
intensityUpdateTimer = IntensityUpdateInterval;
// crew health --------------------------------------------------------
avgCrewHealth = 0.0f;
int characterCount = 0;
foreach (Character character in Character.CharacterList)
{
if (character.IsDead) continue;
#if CLIENT
if ((character.AIController is HumanAIController || character.IsRemotePlayer || character == Character.Controlled) &&
(GameMain.Client?.Character == null || GameMain.Client.Character.TeamID == character.TeamID))
{
avgCrewHealth += character.Vitality / character.MaxVitality * (character.IsUnconscious ? 0.5f : 1.0f);
characterCount++;
}
#endif
}
if (characterCount > 0)
{
avgCrewHealth = avgCrewHealth / characterCount;
}
// enemy amount --------------------------------------------------------
enemyDanger = 0.0f;
foreach (Character character in Character.CharacterList)
{
if (character.IsDead || character.IsUnconscious || !character.Enabled) continue;
EnemyAIController enemyAI = character.AIController as EnemyAIController;
if (enemyAI == null) continue;
if (character.CurrentHull?.Submarine != null &&
(character.CurrentHull.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(character.CurrentHull.Submarine)))
{
//crawler inside the sub adds 0.1f to enemy danger, mantis 0.25f
enemyDanger += enemyAI.CombatStrength / 1000.0f;
}
else if (enemyAI.SelectedAiTarget?.Entity?.Submarine != null)
{
//enemy outside and targeting the sub or something in it
//moloch adds 0.24 to enemy danger, a crawler 0.02
enemyDanger += enemyAI.CombatStrength / 5000.0f;
}
}
enemyDanger = MathHelper.Clamp(enemyDanger, 0.0f, 1.0f);
// hull status (gaps, flooding, fire) --------------------------------------------------------
float holeCount = 0.0f;
floodingAmount = 0.0f;
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine == null || hull.Submarine.IsOutpost) { continue; }
foreach (Gap gap in hull.ConnectedGaps)
{
if (!gap.IsRoomToRoom) holeCount += gap.Open;
}
floodingAmount += hull.WaterVolume / hull.Volume / Hull.hullList.Count;
fireAmount += hull.FireSources.Sum(fs => fs.Size.X);
}
//hull integrity at 0.0 if there are 10 or more wide-open holes
avgHullIntegrity = MathHelper.Clamp(1.0f - holeCount / 10.0f, 0.0f, 1.0f);
//a fire of any size bumps up the fire amount to 20%
//if the total width of the fires is 1000 or more, the fire amount is considered to be at 100%
fireAmount = MathHelper.Clamp(fireAmount / 1000.0f, fireAmount > 0.0f ? 0.2f : 0.0f, 1.0f);
//flooding less than 10% of the sub is ignored
//to prevent ballast tanks from affecting the intensity
if (floodingAmount < 0.1f) floodingAmount = 0.0f;
// calculate final intensity --------------------------------------------------------
targetIntensity =
((1.0f - avgCrewHealth) + (1.0f - avgHullIntegrity) + floodingAmount) / 3.0f;
targetIntensity += fireAmount * 0.5f;
targetIntensity += enemyDanger;
targetIntensity = MathHelper.Clamp(targetIntensity, 0.0f, 1.0f);
if (targetIntensity > currentIntensity)
{
//50 seconds for intensity to go from 0.0 to 1.0
currentIntensity = MathHelper.Min(currentIntensity + 0.02f * IntensityUpdateInterval, targetIntensity);
}
else
{
//400 seconds for intensity to go from 1.0 to 0.0
currentIntensity = MathHelper.Max(0.0025f * IntensityUpdateInterval, targetIntensity);
}
}
}
}