1 using Microsoft.Xna.Framework;
2 using System.Collections.Generic;
39 [
Serialize(
true,
IsPropertySaveable.Yes, description:
"If true, characters who are being targeted by some enemy cannot trigger the action.")]
48 [
Serialize(
false,
IsPropertySaveable.Yes, description:
"If true, the action can be triggered by interacting with any matching target (not just the 1st one).")]
51 [
Serialize(
false,
IsPropertySaveable.Yes, description:
"If true and using multiple targets, all targets must be inside/outside the radius.")]
57 private float distance;
63 DebugConsole.AddWarning(
64 $
"Potential error in {nameof(TriggerAction)}, event \"{parentEvent.Prefab.Identifier}\": "+
65 $
"{nameof(TagAction.IgnoreIncapacitatedCharacters)} is a property of {nameof(TagAction)}, did you mean {nameof(DisableIfTargetIncapacitated)}?",
70 private bool isFinished =
false;
84 private readonly List<Either<Character, Item>> npcsOrItems =
new List<Either<Character, Item>>();
88 public override void Update(
float deltaTime)
90 if (isFinished) {
return; }
95 if (!targets1.Any()) {
return; }
98 foreach (
Entity e1
in targets1)
125 if (CheckDistanceToHull(e1, out hull))
127 triggerers.Add((e1, hull));
139 foreach (
Entity e2
in targets2)
202 if (!npcsOrItems.Any(n => n.TryGet(out
Character npc2) && npc2 == npc))
204 npcsOrItems.Add(npc);
217 (speaker, player) => {
if (e1 == speaker) { Trigger(speaker, player); }
else { Trigger(player, speaker); } },
218 TextManager.GetWithVariable(
"CampaignInteraction.Examine",
"[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.Use)));
221 (speaker, player) => {
if (e1 == speaker) { Trigger(speaker, player); }
else { Trigger(player, speaker); } },
222 TextManager.Get(
"CampaignInteraction.Talk"));
228 else if (item !=
null)
230 if (!npcsOrItems.Any(n => n.TryGet(out
Item item2) && item2 == item))
232 npcsOrItems.Add(item);
235 GameMain.
NetworkMember?.ConnectedClients.Where(c => c.Character !=
null && targets2.Contains(c.Character)));
249 Vector2 pos1 = e1.WorldPosition;
251 distance = Vector2.Distance(pos1, pos2);
261 triggerers.Add((e1, e2));
269 bool IsWithinRadius() =>
277 foreach (var (e1, e2) in triggerers)
283 private void ResetTargetIcons()
285 foreach (var npcOrItem
in npcsOrItems)
290 npc.SetCustomInteract(
null,
null);
291 npc.RequireConsciousnessForCustomInteract =
true;
296 else if (npcOrItem.TryGet(out Item item))
298 item.AssignCampaignInteractionType(CampaignMode.InteractionType.None);
303 private bool CheckDistanceToHull(Entity e, out Hull hull)
308 if (e is Character character && character.CurrentHull !=
null && character.CurrentHull.OutpostModuleTags.Contains(
TargetModuleType))
310 hull = character.CurrentHull;
313 else if (e is Item item && item.CurrentHull !=
null && item.CurrentHull.OutpostModuleTags.Contains(
TargetModuleType))
315 hull = item.CurrentHull;
322 foreach (Hull potentialHull
in Hull.HullList)
324 if (!potentialHull.OutpostModuleTags.Contains(
TargetModuleType)) {
continue; }
325 Rectangle hullRect = potentialHull.WorldRect;
327 if (
Submarine.RectContains(hullRect, e.WorldPosition))
329 hull = potentialHull;
337 private static bool IsInCombat(Entity entity)
339 if (entity is not Character character) {
return false; }
340 foreach (Character c
in Character.CharacterList)
342 if (c.IsDead || c.Removed || c.IsIncapacitated || !c.Enabled) {
continue; }
343 if (c.IsBot && c.AIController is HumanAIController humanAi)
345 if (humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective &&
346 combatObjective.Enemy == character)
351 else if (c.AIController is EnemyAIController { State: AIState.Aggressive or AIState.Attack } enemyAI)
353 if (enemyAI.SelectedAiTarget?.Entity == character || c.CurrentHull == character.CurrentHull)
362 private void Trigger(Entity entity1, Entity entity2)
375 Entity target =
null;
376 if (entity1 is Character { IsPlayer:
true })
381 else if (entity2 is Character { IsPlayer:
true })
388 if (target is Character targetCharacter)
390 player.SelectCharacter(targetCharacter);
392 else if (target is Item targetItem)
394 if (targetItem.IsSecondaryItem)
396 player.SelectedSecondaryItem = targetItem;
400 player.SelectedItem = targetItem;
413 string targetStr =
"none";
414 if (npcsOrItems.Any())
416 targetStr =
string.Join(
", ",
417 npcsOrItems.Select(npcOrItem =>
418 npcOrItem.TryGet(out
Character character) ? character.Name : (npcOrItem.TryGet(out
Item item) ? item.Name :
"none")));
422 $
"{ToolBox.GetDebugSymbol(isFinished, isRunning)} {nameof(TriggerAction)} -> (" +
424 $
"Selected non-player target: {targetStr.ColorizeObject()}, " :
425 $
"Distance: {((int)distance).ColorizeObject()}, ") +
426 $
"Radius: {Radius.ColorizeObject()}, " +
427 $
"TargetTags: {Target1Tag.ColorizeObject()}, " +
428 $
"{Target2Tag.ColorizeObject()})";
432 return $
"{ToolBox.GetDebugSymbol(isFinished, isRunning)} {nameof(TriggerAction)} -> (TargetTags: {Target1Tag.ColorizeObject()}, {TargetModuleType.ColorizeObject()})";
bool IsKeyHit(InputType inputType)
bool RequireConsciousnessForCustomInteract
void SetCustomInteract(Action< Character, Character > onCustomInteract, LocalizedString hudText)
Set an action that's invoked when another character interacts with this one.
CampaignMode.InteractionType CampaignInteractionType
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
CharacterInventory Inventory
Item SelectedSecondaryItem
The secondary selected item. It's an item other than a device (see SelectedItem), e....
ContentPackage? ContentPackage
XAttribute? GetAttribute(string name)
virtual Vector2 WorldPosition
readonly ScriptedEvent ParentEvent
static NetworkMember NetworkMember
bool Contains(Item item)
Is the item contained in this inventory. Does not recursively check items inside items.
void AssignCampaignInteractionType(CampaignMode.InteractionType interactionType, IEnumerable< Client > targetClients=null)
void AddTarget(Identifier tag, Entity target)
IEnumerable< Entity > GetTargets(Identifier tag)
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
Tags a specific entity. Tags are used by other actions to refer to specific entities....
bool IgnoreIncapacitatedCharacters
Waits for a player to trigger the action before continuing. Triggering can mean entering a specific t...
override bool IsFinished(ref string goTo)
Has the action finished.
override void Update(float deltaTime)
override string ToDebugString()
Rich test to display in debugdraw
bool DisableIfTargetIncapacitated
Identifier ApplyToTarget2
Identifier TargetModuleType
TriggerAction(ScriptedEvent parentEvent, ContentXElement element)
Identifier ApplyToTarget1
bool AllowMultipleTargets
@ Character
Characters only