Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Events/ScriptedEvent.cs
2023-03-13 10:30:37 +02:00

243 lines
8.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
class ScriptedEvent : Event
{
private readonly Dictionary<Identifier, List<Predicate<Entity>>> targetPredicates = new Dictionary<Identifier, List<Predicate<Entity>>>();
private readonly Dictionary<Identifier, List<Entity>> cachedTargets = new Dictionary<Identifier, List<Entity>>();
private int prevEntityCount;
private int prevPlayerCount, prevBotCount;
private Character prevControlled;
private readonly string[] requiredDestinationTypes;
public readonly bool RequireBeaconStation;
public int CurrentActionIndex { get; private set; }
public List<EventAction> Actions { get; } = new List<EventAction>();
public Dictionary<Identifier, List<Entity>> Targets { get; } = new Dictionary<Identifier, List<Entity>>();
public override string ToString()
{
return $"ScriptedEvent ({prefab.Identifier})";
}
public ScriptedEvent(EventPrefab prefab) : base(prefab)
{
foreach (var element in prefab.ConfigElement.Elements())
{
if (element.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase))
{
DebugConsole.ThrowError($"Error in event prefab \"{prefab.Identifier}\". Status effect configured as an action. Please configure status effects as child elements of a StatusEffectAction.");
continue;
}
var action = EventAction.Instantiate(this, element);
if (action != null) { Actions.Add(action); }
}
if (!Actions.Any())
{
DebugConsole.ThrowError($"Scripted event \"{prefab.Identifier}\" has no actions. The event will do nothing.");
}
requiredDestinationTypes = prefab.ConfigElement.GetAttributeStringArray("requireddestinationtypes", null);
RequireBeaconStation = prefab.ConfigElement.GetAttributeBool("requirebeaconstation", false);
GameAnalyticsManager.AddDesignEvent($"ScriptedEvent:{prefab.Identifier}:Start");
}
public void AddTarget(Identifier tag, Entity target)
{
if (target == null)
{
throw new System.ArgumentException("Target was null");
}
if (target.Removed)
{
throw new System.ArgumentException("Target has been removed");
}
if (!Targets.ContainsKey(tag))
{
Targets.Add(tag, new List<Entity>());
}
Targets[tag].Add(target);
if (cachedTargets.ContainsKey(tag))
{
cachedTargets[tag].Add(target);
}
else
{
cachedTargets.Add(tag, new List<Entity> { target });
}
}
public void AddTargetPredicate(Identifier tag, Predicate<Entity> predicate)
{
if (!targetPredicates.ContainsKey(tag))
{
targetPredicates.Add(tag, new List<Predicate<Entity>>());
}
targetPredicates[tag].Add(predicate);
// force re-search for this tag
if (cachedTargets.ContainsKey(tag))
{
cachedTargets.Remove(tag);
}
}
public IEnumerable<Entity> GetTargets(Identifier tag)
{
if (cachedTargets.ContainsKey(tag))
{
if (cachedTargets[tag].Any(t => t.Removed))
{
cachedTargets.Clear();
}
else
{
return cachedTargets[tag];
}
}
List<Entity> targetsToReturn = new List<Entity>();
if (Targets.ContainsKey(tag))
{
foreach (Entity e in Targets[tag])
{
if (e.Removed) { continue; }
targetsToReturn.Add(e);
}
}
if (targetPredicates.ContainsKey(tag))
{
foreach (Entity entity in Entity.GetEntities())
{
if (targetPredicates[tag].Any(p => p(entity)) && !targetsToReturn.Contains(entity))
{
targetsToReturn.Add(entity);
}
}
}
foreach (WayPoint wayPoint in WayPoint.WayPointList)
{
if (wayPoint.Tags.Contains(tag)) { targetsToReturn.Add(wayPoint); }
}
if (Level.Loaded?.StartOutpost != null &&
Level.Loaded.StartOutpost.Info.OutpostNPCs.TryGetValue(tag, out List<Character> outpostNPCs))
{
foreach (Character npc in outpostNPCs)
{
if (npc.Removed || targetsToReturn.Contains(npc)) { continue; }
targetsToReturn.Add(npc);
}
}
cachedTargets.Add(tag, targetsToReturn);
return targetsToReturn;
}
public void RemoveTag(Identifier tag)
{
if (tag.IsEmpty) { return; }
if (Targets.ContainsKey(tag)) { Targets.Remove(tag); }
if (cachedTargets.ContainsKey(tag)) { cachedTargets.Remove(tag); }
if (targetPredicates.ContainsKey(tag)) { targetPredicates.Remove(tag); }
}
public override void Update(float deltaTime)
{
int botCount = 0;
int playerCount = 0;
foreach (Character c in Character.CharacterList)
{
if (c.IsPlayer)
{
playerCount++;
}
else if (c.IsBot)
{
botCount++;
}
}
if (Entity.EntityCount != prevEntityCount || botCount != prevBotCount || playerCount != prevPlayerCount || prevControlled != Character.Controlled)
{
cachedTargets.Clear();
prevEntityCount = Entity.EntityCount;
prevBotCount = botCount;
prevPlayerCount = playerCount;
prevControlled = Character.Controlled;
}
if (!Actions.Any())
{
Finish();
return;
}
var currentAction = Actions[CurrentActionIndex];
if (!currentAction.CanBeFinished())
{
Finish();
return;
}
string goTo = null;
if (currentAction.IsFinished(ref goTo))
{
if (string.IsNullOrEmpty(goTo))
{
CurrentActionIndex++;
}
else
{
CurrentActionIndex = -1;
Actions.ForEach(a => a.Reset());
for (int i = 0; i < Actions.Count; i++)
{
if (Actions[i].SetGoToTarget(goTo))
{
CurrentActionIndex = i;
break;
}
}
}
if (CurrentActionIndex >= Actions.Count || CurrentActionIndex < 0)
{
Finish();
}
}
else
{
currentAction.Update(deltaTime);
}
}
public override bool LevelMeetsRequirements()
{
if (requiredDestinationTypes == null) { return true; }
var currLocation = GameMain.GameSession?.Campaign?.Map.CurrentLocation;
if (currLocation?.Connections == null) { return true; }
foreach (LocationConnection c in currLocation.Connections)
{
if (RequireBeaconStation && !c.LevelData.HasBeaconStation) { continue; }
if (requiredDestinationTypes.Any(t => c.OtherLocation(currLocation).Type.Identifier == t))
{
return true;
}
}
return false;
}
public override void Finish()
{
base.Finish();
GameAnalyticsManager.AddDesignEvent($"ScriptedEvent:{prefab.Identifier}:Finished:{CurrentActionIndex}");
}
}
}