3 using System.Collections.Generic;
12 Predicate<Entity> Predicate)
14 public enum EntityType
24 private readonly Dictionary<Identifier, List<TargetPredicate>> targetPredicates =
new Dictionary<Identifier, List<TargetPredicate>>();
26 private readonly Dictionary<Identifier, List<Entity>> cachedTargets =
new Dictionary<Identifier, List<Entity>>();
32 private readonly Dictionary<Identifier, int> initialAmounts =
new Dictionary<Identifier, int>();
34 private bool newEntitySpawned;
35 private int prevPlayerCount, prevBotCount;
40 private readonly
string[] requiredDestinationTypes;
44 public List<EventAction>
Actions {
get; } =
new List<EventAction>();
45 public Dictionary<Identifier, List<Entity>>
Targets {
get; } =
new Dictionary<Identifier, List<Entity>>();
51 return $
"{nameof(ScriptedEvent)} ({prefab.Identifier})";
58 Identifier elementId = element.Name.ToIdentifier();
65 if (elementId ==
"statuseffect")
67 DebugConsole.ThrowError($
"Error in event prefab \"{prefab.Identifier}\". Status effect configured as an action. Please configure status effects as child elements of a StatusEffectAction.",
72 if (action !=
null) {
Actions.Add(action); }
77 DebugConsole.ThrowError($
"Scripted event \"{prefab.Identifier}\" has no actions. The event will do nothing.",
85 foreach (var gotoAction
in allActions.OfType<
GoTo>())
87 if (allActions.None(a => a is
Label label && label.
Name == gotoAction.Name))
89 DebugConsole.ThrowError($
"Error in event \"{prefab.Identifier}\". Could not find a label matching the GoTo \"{gotoAction.Name}\".",
94 GameAnalyticsManager.AddDesignEvent($
"ScriptedEvent:{prefab.Identifier}:Start");
101 string text = $
"Finished: {IsFinished.ColorizeObject()}\n" +
102 $
"Action index: {CurrentActionIndex.ColorizeObject()}\n" +
103 $
"Current action: {currentAction?.ToDebugString() ?? ToolBox.ColorizeObject(null)}\n";
105 text +=
"All actions:\n";
106 text +=
GetAllActions().Aggregate(
string.Empty, (current, action) => current + $
"{new string(' ', action.indent * 6)}{action.action.ToDebugString()}\n");
108 text +=
"Targets:\n";
109 foreach (var (key, value) in
Targets)
111 text += $
" {key.ColorizeObject()}: {value.Aggregate(string.Empty, (current, entity) => current + $"{entity.ColorizeObject()}
")}\n";
118 if (tag.StartsWith(
"eventtag:"))
120 string targetTag = tag[
"eventtag:".Length..];
124 if (target is
Item item) {
return item.Name; }
125 if (target is
Character character) {
return character.Name; }
126 if (target is
Hull hull) {
return hull.DisplayName.Value; }
127 if (target is
Submarine sub) {
return sub.Info.DisplayName.Value; }
128 DebugConsole.AddWarning($
"Failed to get the name of the event target {target} as a replacement for the tag {tag} in an event text.",
130 return target.ToString();
134 return $
"[target \"{targetTag}\" not found]";
151 var list =
new List<(int indent, EventAction action)>();
154 list.AddRange(FindActionsRecursive(eventAction));
158 static List<(
int indent,
EventAction action)> FindActionsRecursive(
EventAction eventAction,
int indent = 1)
160 var eventActions =
new List<(
int indent,
EventAction action)> { (indent, eventAction) };
164 eventActions.AddRange(FindActionsRecursive(action, indent));
174 throw new ArgumentException($
"Target was null (tag: {tag})");
178 throw new ArgumentException($
"Target has been removed (tag: {tag})");
182 if (!
Targets[tag].Contains(target))
189 Targets.Add(tag,
new List<Entity>() { target });
191 if (cachedTargets.ContainsKey(tag))
193 if (!cachedTargets[tag].Contains(target))
195 cachedTargets[tag].Add(target);
200 cachedTargets.Add(tag,
Targets[tag].ToList());
202 if (!initialAmounts.ContainsKey(tag))
204 initialAmounts.Add(tag, cachedTargets[tag].Count);
210 if (!targetPredicates.ContainsKey(tag))
212 targetPredicates.Add(tag,
new List<TargetPredicate>());
216 if (cachedTargets.ContainsKey(tag))
218 cachedTargets.Remove(tag);
224 if (initialAmounts.TryGetValue(tag, out
int count))
233 if (cachedTargets.ContainsKey(tag))
235 if (cachedTargets[tag].
Any(t => t.Removed))
237 cachedTargets.Clear();
241 return cachedTargets[tag];
245 List<Entity> targetsToReturn =
new List<Entity>();
251 targetsToReturn.Add(e);
254 if (targetPredicates.ContainsKey(tag))
256 foreach (var targetPredicate
in targetPredicates[tag])
258 IEnumerable<Entity> entityList = targetPredicate.Type
switch
267 foreach (
Entity entity
in entityList)
269 if (targetsToReturn.Contains(entity)) {
continue; }
270 if (targetPredicate.Predicate(entity))
272 targetsToReturn.Add(entity);
279 if (wayPoint.
Tags.Contains(tag)) { targetsToReturn.Add(wayPoint); }
286 if (npc.
Removed || targetsToReturn.Contains(npc)) {
continue; }
287 targetsToReturn.Add(npc);
291 cachedTargets.Add(tag, targetsToReturn);
292 if (!initialAmounts.ContainsKey(tag))
294 initialAmounts.Add(tag, targetsToReturn.Count);
296 return targetsToReturn;
303 if (kvp.Value.Contains(originalEntity))
305 kvp.Value.Add(newEntity);
312 if (tag.IsEmpty) {
return; }
314 if (cachedTargets.ContainsKey(tag)) { cachedTargets.Remove(tag); }
315 if (targetPredicates.ContainsKey(tag)) { targetPredicates.Remove(tag); }
318 public override void Update(
float deltaTime)
334 if (botCount != prevBotCount || playerCount != prevPlayerCount || prevControlled !=
Character.
Controlled || NeedsToRefreshCachedTargets())
336 cachedTargets.Clear();
337 newEntitySpawned =
false;
338 prevBotCount = botCount;
339 prevPlayerCount = playerCount;
350 if (!currentAction.CanBeFinished())
357 if (currentAction.IsFinished(ref goTo))
359 if (
string.IsNullOrEmpty(goTo))
366 Actions.ForEach(a => a.Reset());
367 for (
int i = 0; i <
Actions.Count; i++)
369 if (
Actions[i].SetGoToTarget(goTo))
377 DebugConsole.AddWarning($
"Could not find the GoTo label \"{goTo}\" in the event \"{Prefab.Identifier}\". Ending the event.",
389 currentAction.Update(deltaTime);
393 private bool NeedsToRefreshCachedTargets()
395 if (newEntitySpawned) {
return true; }
396 foreach (var cachedTargetList
in cachedTargets.Values)
398 foreach (var target
in cachedTargetList)
412 if (newEntitySpawned) {
return; }
417 newEntitySpawned =
true;
421 foreach (var targetPredicateList
in targetPredicates.Values)
423 foreach (var targetPredicate
in targetPredicateList)
425 if (targetPredicate.Predicate(entity))
427 newEntitySpawned =
true;
436 if (requiredDestinationTypes ==
null) {
return true; }
438 if (currLocation?.Connections ==
null) {
return true; }
453 GameAnalyticsManager.AddDesignEvent($
"ScriptedEvent:{prefab.Identifier}:Finished:{CurrentActionIndex}");
static readonly List< Character > CharacterList
static Character? Controlled
IEnumerable< ContentXElement > Elements()
bool GetAttributeBool(string key, bool def)
string?[] GetAttributeStringArray(string key, string[]? def, bool convertToLowerInvariant=false)
static IReadOnlyCollection< Entity > GetEntities()
virtual IEnumerable< EventAction > GetSubActions()
static EventAction Instantiate(ScriptedEvent scriptedEvent, ContentXElement element)
readonly EventPrefab prefab
readonly ContentXElement ConfigElement
static GameSession?? GameSession
Makes the event jump to a Label somewhere else in the event.
static readonly List< Hull > HullList
static readonly List< Item > ItemList
Defines a point in the event that GoTo actions can jump to.
Location OtherLocation(Location location)
static readonly List< MapEntity > MapEntityList
Executes all the child actions when the round ends.
ContentPackage? ContentPackage
readonly Identifier Identifier
virtual IEnumerable< Identifier > NonActionChildElementNames
override string ToString()
override string GetDebugInfo()
int GetInitialTargetCount(Identifier tag)
ScriptedEvent(EventPrefab prefab, int seed)
sealed record TargetPredicate(TargetPredicate.EntityType Type, Predicate< Entity > Predicate)
readonly OnRoundEndAction OnRoundEndAction
void InheritTags(Entity originalEntity, Entity newEntity)
override void Update(float deltaTime)
List<(int indent, EventAction action)> GetAllActions()
Finds all actions in the ScriptedEvent (recursively going through the subactions as well)....
virtual LocalizedString ReplaceVariablesInEventText(LocalizedString str)
readonly bool RequireBeaconStation
void RemoveTag(Identifier tag)
void AddTargetPredicate(Identifier tag, TargetPredicate.EntityType entityType, Predicate< Entity > predicate)
Dictionary< Identifier, List< Entity > > Targets
void EntitySpawned(Entity entity)
void AddTarget(Identifier tag, Entity target)
override bool LevelMeetsRequirements()
List< EventAction > Actions
IEnumerable< Entity > GetTargets(Identifier tag)
virtual string GetTextForReplacementElement(string tag)
static List< Submarine > Loaded
readonly Dictionary< Identifier, List< Character > > OutpostNPCs
static List< WayPoint > WayPointList
IEnumerable< Identifier > Tags