3 using System.Collections.Generic;
4 using System.Collections.Immutable;
16 [
Serialize(
"",
IsPropertySaveable.Yes, description:
"What criteria to use to select the entities to target. Valid values are players, player, traitor, nontraitor, nontraitorplayer, bot, crew, humanprefabidentifier:[id], jobidentifier:[id], structureidentifier:[id], structurespecialtag:[tag], itemidentifier:[id], itemtag:[tag], hull, hullname:[name], submarine:[type], eventtag:[tag].")]
20 public Identifier
Tag {
get;
set; }
28 [
Serialize(
true,
IsPropertySaveable.Yes, description:
"Should incapacitated (e.g. dead, paralyzed, unconscious) characters be ignored, i.e. not considered valid targets?")]
34 [
Serialize(
false,
IsPropertySaveable.Yes, description:
"If there are multiple matching targets, should all of them be tagged or one chosen randomly?")]
40 [
Serialize(0.0f,
IsPropertySaveable.Yes, description:
"If larger than 0, the specified percentage of the matching targets are tagged. Between 0-100.")]
43 private bool isFinished =
false;
49 private bool cantFindTargets =
false;
55 private bool mustRecheckTargets =
false;
57 private bool taggingDone =
false;
61 Taggers =
new (
string k, Action<Identifier> v)[]
63 (
"players", v => TagPlayers()),
64 (
"player", v => TagPlayers()),
65 (
"traitor", v => TagTraitors()),
66 (
"nontraitor", v => TagNonTraitors()),
67 (
"nontraitorplayer", v => TagNonTraitorPlayers()),
68 (
"bot", v => TagBots(playerCrewOnly:
false)),
69 (
"crew", v => TagCrew()),
70 (
"humanprefabidentifier", TagHumansByIdentifier),
71 (
"humanprefabtag", TagHumansByTag),
72 (
"jobidentifier", TagHumansByJobIdentifier),
73 (
"structureidentifier", TagStructuresByIdentifier),
74 (
"structurespecialtag", TagStructuresBySpecialTag),
75 (
"itemidentifier", TagItemsByIdentifier),
76 (
"itemtag", TagItemsByTag),
77 (
"hull", v => TagHulls()),
78 (
"hullname", TagHullsByName),
79 (
"submarine", TagSubmarinesByType),
80 (
"eventtag", TagByEventTag),
81 }.Select(t => (t.k.ToIdentifier(), t.v)).ToImmutableDictionary();
93 private void TagByEventTag(Identifier eventTag)
98 private void TagPlayers()
102 ScriptedEvent.TargetPredicate.EntityType.Character,
106 private void TagTraitors()
110 ScriptedEvent.TargetPredicate.EntityType.Character,
111 e => e is Character c && (c.IsPlayer || c.IsBot) && c.IsTraitor && !c.IsIncapacitated);
114 private void TagNonTraitors()
118 ScriptedEvent.TargetPredicate.EntityType.Character,
119 e => e is Character c && (c.IsPlayer || c.IsBot) && !c.IsTraitor && c.IsOnPlayerTeam && !c.IsIncapacitated);
122 private void TagNonTraitorPlayers()
125 Tags.NonTraitorPlayer,
126 ScriptedEvent.TargetPredicate.EntityType.Character,
127 e => e is Character c && c.IsPlayer && !c.IsTraitor && c.IsOnPlayerTeam && !c.IsIncapacitated);
130 private void TagBots(
bool playerCrewOnly)
134 ScriptedEvent.TargetPredicate.EntityType.Character,
142 private void TagCrew()
145 AddTarget(
Tag, GameMain.GameSession.CrewManager.GetCharacters());
148 TagBots(playerCrewOnly:
true);
152 private void TagHumansByIdentifier(Identifier identifier)
154 AddTarget(
Tag,
Character.CharacterList.Where(c => c.HumanPrefab?.Identifier == identifier));
157 private void TagHumansByTag(Identifier tag)
159 AddTarget(
Tag,
Character.CharacterList.Where(c => c.HumanPrefab !=
null && c.HumanPrefab.GetTags().Contains(tag)));
162 private void TagHumansByJobIdentifier(Identifier jobIdentifier)
164 AddTarget(
Tag,
Character.CharacterList.Where(c => c.HasJob(jobIdentifier)));
167 private void TagStructuresByIdentifier(Identifier identifier)
171 ScriptedEvent.TargetPredicate.EntityType.Structure,
172 e => e is Structure s && MatchesRequirements(s) && s.Prefab.Identifier == identifier);
175 private void TagStructuresBySpecialTag(Identifier tag)
179 ScriptedEvent.TargetPredicate.EntityType.Structure,
180 e => e is Structure s && MatchesRequirements(s) && s.SpecialTag.ToIdentifier() == tag);
183 private void TagItemsByIdentifier(Identifier identifier)
187 ScriptedEvent.TargetPredicate.EntityType.Item,
188 e => e is Item it && it.Prefab.Identifier == identifier && IsValidItem(it));
191 private void TagItemsByTag(Identifier tag)
195 ScriptedEvent.TargetPredicate.EntityType.Item,
196 e => e is Item it && it.HasTag(tag) && IsValidItem(it));
199 private void TagHulls()
203 ScriptedEvent.TargetPredicate.EntityType.Hull,
204 e => e is Hull h && MatchesRequirements(h));
207 private void TagHullsByName(Identifier name)
211 ScriptedEvent.TargetPredicate.EntityType.Hull,
212 e => e is Hull h && MatchesRequirements(h) && h.RoomName.Contains(name.Value, StringComparison.OrdinalIgnoreCase));
215 private void TagSubmarinesByType(Identifier type)
219 ScriptedEvent.TargetPredicate.EntityType.Submarine,
220 e => e is Submarine s && MatchesRequirements(s) && (type.IsEmpty || type == s.Info?.Type.ToIdentifier()));
223 private bool IsValidItem(Item it)
228 ModuleTagMatches(it) &&
231 SubmarineTypeMatches(it.Submarine ?? it.CurrentHull?.Submarine ?? it.ParentInventory?.Owner?.Submarine);
234 private bool MatchesRequirements(Entity e)
236 return ModuleTagMatches(e) && SubmarineTypeMatches(e.Submarine);
239 private bool ModuleTagMatches(Entity e)
242 if (e?.Submarine ==
null) {
return false; }
245 if (e is Character character)
247 hull = character.CurrentHull;
249 else if (e is Item item)
251 hull = item.CurrentHull;
253 else if (e is WayPoint wp)
255 hull = wp.CurrentHull;
257 else if (e is Hull h)
263 DebugConsole.AddWarning($
"Potential error in event \"{ParentEvent.Prefab.Identifier}\": {nameof(TagAction)} cannot check the module tags of an entity of the type {e.GetType()}.");
271 private bool SubmarineTypeMatches(Submarine sub)
278 if (submarineType ==
SubType.Any) {
return true; }
279 if (sub ==
null) {
return false; }
286 return submarineType.HasFlag(
SubType.Outpost);
288 return submarineType.HasFlag(
SubType.Wreck);
290 return submarineType.HasFlag(
SubType.BeaconStation);
304 TagRandom(tag, Entity.GetEntities().Where(e => predicate(e)));
309 mustRecheckTargets =
true;
313 private void AddTarget(Identifier tag, IEnumerable<Entity> entities)
317 cantFindTargets =
true;
322 TagPercentage(tag, entities);
326 TagRandom(tag, entities);
330 foreach (var entity
in entities)
337 private List<Entity> tempEntities;
338 private void TagPercentage(Identifier tag, IEnumerable<Entity> entities)
342 cantFindTargets =
true;
346 int amountToChoose = (int)Math.Ceiling(entities.Count() * (
ChoosePercentage / 100.0f));
348 tempEntities ??=
new List<Entity>();
349 tempEntities.Clear();
350 for (
int i = 0; i < amountToChoose; i++)
352 var entity = entities.GetRandomUnsynced();
353 tempEntities.Remove(entity);
358 private void TagRandom(Identifier tag, IEnumerable<Entity> entities)
362 cantFindTargets =
true;
368 private readonly ImmutableDictionary<Identifier, Action<Identifier>> Taggers;
370 public override void Update(
float deltaTime)
372 if (isFinished || cantFindTargets) {
return; }
376 cantFindTargets =
false;
377 string[] criteriaSplit =
Criteria.Split(
';');
378 foreach (
string entry
in criteriaSplit)
380 string[] kvp = entry.Split(
':');
381 Identifier key = kvp[0].Trim().ToIdentifier();
382 Identifier value = kvp.Length > 1 ? kvp[1].Trim().ToIdentifier() : Identifier.Empty;
383 if (Taggers.TryGetValue(key, out Action<Identifier> tagger))
389 string errorMessage = $
"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
390 DebugConsole.ThrowError(errorMessage,
392 GameAnalyticsManager.AddErrorEventOnce($
"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
404 if (mustRecheckTargets)
410 isFinished = !cantFindTargets;
417 return $
"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()}, Sub: {SubmarineType.ColorizeObject()})";
static IReadOnlyCollection< Entity > GetEntities()
readonly ScriptedEvent ParentEvent
static NetworkMember NetworkMember
ContentPackage? ContentPackage
sealed record TargetPredicate(TargetPredicate.EntityType Type, Predicate< Entity > Predicate)
void AddTargetPredicate(Identifier tag, TargetPredicate.EntityType entityType, Predicate< Entity > predicate)
void AddTarget(Identifier tag, Entity target)
IEnumerable< Entity > GetTargets(Identifier tag)
Tags a specific entity. Tags are used by other actions to refer to specific entities....
bool IgnoreIncapacitatedCharacters
static bool SubmarineTypeMatches(Submarine sub, SubType submarineType)
override bool IsFinished(ref string goTo)
Has the action finished.
TagAction(ScriptedEvent parentEvent, ContentXElement element)
bool ContinueIfNoTargetsFound
Identifier RequiredModuleTag
override string ToDebugString()
Rich test to display in debugdraw
override void Update(float deltaTime)