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;
61 private bool isFinished =
false;
75 private readonly List<Either<Character, Item>> npcsOrItems =
new List<Either<Character, Item>>();
79 public override void Update(
float deltaTime)
81 if (isFinished) {
return; }
86 if (!targets1.Any()) {
return; }
89 foreach (
Entity e1
in targets1)
116 if (CheckDistanceToHull(e1, out hull))
118 triggerers.Add((e1, hull));
130 foreach (
Entity e2
in targets2)
200 if (!npcsOrItems.Any(n => n.TryGet(out
Character npc2) && npc2 == npc))
202 npcsOrItems.Add(npc);
208 (speaker, player) => {
if (e1 == speaker) { Trigger(speaker, player); }
else { Trigger(player, speaker); } },
209 TextManager.GetWithVariable(
"CampaignInteraction.Examine",
"[key]", GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.Use)));
212 (speaker, player) => {
if (e1 == speaker) { Trigger(speaker, player); }
else { Trigger(player, speaker); } },
213 TextManager.Get(
"CampaignInteraction.Talk"));
219 else if (item !=
null)
221 if (!npcsOrItems.Any(n => n.TryGet(out
Item item2) && item2 == item))
223 npcsOrItems.Add(item);
226 GameMain.
NetworkMember?.ConnectedClients.Where(c => c.Character !=
null && targets2.Contains(c.Character)));
240 Vector2 pos1 = e1.WorldPosition;
242 distance = Vector2.Distance(pos1, pos2);
252 triggerers.Add((e1, e2));
260 bool IsWithinRadius() =>
268 foreach (var (e1, e2) in triggerers)
274 private void ResetTargetIcons()
276 foreach (var npcOrItem
in npcsOrItems)
281 npc.SetCustomInteract(
null,
null);
282 npc.RequireConsciousnessForCustomInteract =
true;
287 else if (npcOrItem.TryGet(out Item item))
289 item.AssignCampaignInteractionType(CampaignMode.InteractionType.None);
294 private bool CheckDistanceToHull(Entity e, out Hull hull)
299 if (e is Character character && character.CurrentHull !=
null && character.CurrentHull.OutpostModuleTags.Contains(
TargetModuleType))
301 hull = character.CurrentHull;
304 else if (e is Item item && item.CurrentHull !=
null && item.CurrentHull.OutpostModuleTags.Contains(
TargetModuleType))
306 hull = item.CurrentHull;
313 foreach (Hull potentialHull
in Hull.HullList)
315 if (!potentialHull.OutpostModuleTags.Contains(
TargetModuleType)) {
continue; }
316 Rectangle hullRect = potentialHull.WorldRect;
318 if (
Submarine.RectContains(hullRect, e.WorldPosition))
320 hull = potentialHull;
328 private static bool IsInCombat(Entity entity)
330 if (entity is not Character character) {
return false; }
331 foreach (Character c
in Character.CharacterList)
333 if (c.IsDead || c.Removed || c.IsIncapacitated || !c.Enabled) {
continue; }
334 if (c.IsBot && c.AIController is HumanAIController humanAi)
336 if (humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective &&
337 combatObjective.Enemy == character)
342 else if (c.AIController is EnemyAIController enemyAI && (enemyAI.State ==
AIState.Aggressive || enemyAI.State ==
AIState.Attack))
344 if (enemyAI.SelectedAiTarget?.Entity == character || c.CurrentHull == character.CurrentHull)
353 private void Trigger(Entity entity1, Entity entity2)
366 Entity target =
null;
367 if (entity1 is Character { IsPlayer:
true })
372 else if (entity2 is Character { IsPlayer:
true })
379 if (target is Character targetCharacter)
381 player.SelectCharacter(targetCharacter);
383 else if (target is Item targetItem)
385 if (targetItem.IsSecondaryItem)
387 player.SelectedSecondaryItem = targetItem;
391 player.SelectedItem = targetItem;
405 $
"{ToolBox.GetDebugSymbol(isFinished, isRunning)} {nameof(TriggerAction)} -> (" +
407 $
"Selected non-player target: {(npcsOrItems?.ToString() ?? "<
null>
").ColorizeObject()}, " :
408 $
"Distance: {((int)distance).ColorizeObject()}, ") +
409 $
"Radius: {Radius.ColorizeObject()}, " +
410 $
"TargetTags: {Target1Tag.ColorizeObject()}, " +
411 $
"{Target2Tag.ColorizeObject()})";
415 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....
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)
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