243 lines
8.4 KiB
C#
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}");
|
|
}
|
|
}
|
|
}
|