(965c31410a) Unstable v0.10.4.0

This commit is contained in:
Juan Pablo Arce
2020-07-21 08:57:50 -03:00
parent 4f8bd39789
commit 33d3a41104
546 changed files with 45952 additions and 25762 deletions
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class AfflictionAction : EventAction
{
[Serialize("", true)]
public string Affliction { get; set; }
[Serialize(0.0f, true)]
public float Strength { get; set; }
[Serialize(LimbType.None, true)]
public LimbType LimbType { get; set; }
[Serialize("", true)]
public string TargetTag { get; set; }
public AfflictionAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
private bool isFinished = false;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
var afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(p => p.Identifier.Equals(Affliction, StringComparison.InvariantCultureIgnoreCase));
if (afflictionPrefab != null)
{
var targets = ParentEvent.GetTargets(TargetTag);
foreach (var target in targets)
{
if (target != null && target is Character character)
{
var limb = LimbType != LimbType.None ? character.AnimController.GetLimb(LimbType) : null;
if (Strength > 0.0f)
{
character.CharacterHealth.ApplyAffliction(limb, afflictionPrefab.Instantiate(Strength));
}
else if (Strength < 0.0f)
{
character.CharacterHealth.ReduceAffliction(limb, Affliction, -Strength);
}
}
}
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(AfflictionAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
$"Affliction: {Affliction.ColorizeObject()}, Strength: {Strength.ColorizeObject()}, " +
$"LimbType: {LimbType.ColorizeObject()})";
}
}
}
@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
abstract class BinaryOptionAction : EventAction
{
public SubactionGroup Success = null;
public SubactionGroup Failure = null;
protected bool? succeeded = null;
public BinaryOptionAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
foreach (XElement elem in element.Elements())
{
string elemName = elem.Name.LocalName;
if (elemName.Equals("success", StringComparison.InvariantCultureIgnoreCase))
{
Success ??= new SubactionGroup(ParentEvent, elem);
}
else if (elemName.Equals("failure", StringComparison.InvariantCultureIgnoreCase))
{
Failure ??= new SubactionGroup(ParentEvent, elem);
}
}
}
public override IEnumerable<EventAction> GetSubActions()
{
IEnumerable<EventAction> actions = Success?.Actions ?? Enumerable.Empty<EventAction>();
actions = actions.Concat(Failure?.Actions ?? Enumerable.Empty<EventAction>());
return actions;
}
public override bool IsFinished(ref string goTo)
{
return DetermineFinished(ref goTo);
}
protected bool DetermineFinished()
{
string throwaway = null;
return DetermineFinished(ref throwaway);
}
protected bool DetermineFinished(ref string goTo)
{
if (succeeded.HasValue)
{
if (succeeded.Value)
{
if (Success == null || Success.IsFinished(ref goTo))
{
return true;
}
}
else
{
if (Failure == null || Failure.IsFinished(ref goTo))
{
return true;
}
}
}
return false;
}
public override bool SetGoToTarget(string goTo)
{
if (Success != null && Success.SetGoToTarget(goTo))
{
succeeded = true;
return true;
}
else if (Failure != null && Failure.SetGoToTarget(goTo))
{
succeeded = false;
return true;
}
return false;
}
public override void Reset()
{
Success?.Reset();
Failure?.Reset();
succeeded = null;
}
public override void Update(float deltaTime)
{
if (succeeded.HasValue)
{
if (succeeded.Value)
{
Success?.Update(deltaTime);
}
else
{
Failure?.Update(deltaTime);
}
}
else
{
succeeded = DetermineSuccess();
}
}
protected abstract bool? DetermineSuccess();
}
}
@@ -0,0 +1,126 @@
#nullable enable
using System.Xml.Linq;
namespace Barotrauma
{
class CheckDataAction : BinaryOptionAction
{
[Serialize("", true)]
public string Identifier { get; set; } = null!;
[Serialize("", true)]
public string Condition { get; set; } = null!;
protected object? value2;
protected object? value1;
protected PropertyConditional.OperatorType Operator { get; set; }
public CheckDataAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess()
{
if (!(GameMain.GameSession?.GameMode is CampaignMode campaignMode)) { return false; }
string[] splitString = Condition.Split(' ');
string value = Condition;
if (splitString.Length > 0)
{
for (int i = 1; i < splitString.Length; i++)
{
value = splitString[i] + (i > 1 && i < splitString.Length ? " " : "");
}
}
else
{
DebugConsole.ThrowError($"{Condition} is too short, it should start with an operator followed by a boolean or a floating point value.");
return false;
}
string op = splitString[0];
Operator = PropertyConditional.GetOperatorType(op);
if (Operator == PropertyConditional.OperatorType.None) { return false; }
bool? tryBoolean = TryBoolean(campaignMode, value);
if (tryBoolean != null) { return tryBoolean; }
bool? tryFloat = TryFloat(campaignMode, value);
if (tryFloat != null) { return tryFloat; }
DebugConsole.ThrowError($"{value2} ({Condition}) did not match a boolean or a float.");
return false;
}
private bool? TryBoolean(CampaignMode campaignMode, string value)
{
if (bool.TryParse(value, out bool b))
{
bool target = GetBool(campaignMode);
value1 = target;
value2 = b;
switch (Operator)
{
case PropertyConditional.OperatorType.Equals:
return target == b;
case PropertyConditional.OperatorType.NotEquals:
return target != b;
default:
DebugConsole.Log($"Only \"Equals\" and \"Not equals\" operators are allowed for a boolean (was {Operator} for {value}).");
return false;
}
}
DebugConsole.Log($"{value} != bool");
return null;
}
private bool? TryFloat(CampaignMode campaignMode, string value)
{
if (float.TryParse(value, out float f))
{
float target = GetFloat(campaignMode);
value1 = target;
value2 = f;
switch (Operator)
{
case PropertyConditional.OperatorType.Equals:
return MathUtils.NearlyEqual(target, f);
case PropertyConditional.OperatorType.GreaterThan:
return target > f;
case PropertyConditional.OperatorType.GreaterThanEquals:
return target >= f;
case PropertyConditional.OperatorType.LessThan:
return target < f;
case PropertyConditional.OperatorType.LessThanEquals:
return target <= f;
case PropertyConditional.OperatorType.NotEquals:
return !MathUtils.NearlyEqual(target, f);
}
}
DebugConsole.Log($"{value} != float");
return null;
}
protected virtual bool GetBool(CampaignMode campaignMode)
{
return campaignMode.CampaignMetadata.GetBoolean(Identifier);
}
protected virtual float GetFloat(CampaignMode campaignMode)
{
return campaignMode.CampaignMetadata.GetFloat(Identifier);
}
public override string ToDebugString()
{
string condition = "?";
if (value2 != null && value1 != null)
{
condition = $"{value1.ColorizeObject()} {Operator.ColorizeObject()} {value2.ColorizeObject()}";
}
return $"{ToolBox.GetDebugSymbol(succeeded.HasValue)} {nameof(CheckDataAction)} -> (Data: {Identifier.ColorizeObject()}, Success: {succeeded.ColorizeObject()}, Expression: {condition})";
}
}
}
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class CheckItemAction : BinaryOptionAction
{
[Serialize("", true)]
public string TargetTag { get; set; }
[Serialize("", true)]
public string ItemIdentifiers { get; set; }
[Serialize("", true)]
public string ItemTags { get; set; }
private readonly string[] itemIdentifierSplit;
private readonly string[] itemTags;
public CheckItemAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
itemIdentifierSplit = ItemIdentifiers.Split(',');
itemTags = ItemTags.Split(",");
}
protected override bool? DetermineSuccess()
{
var targets = ParentEvent.GetTargets(TargetTag);
if (!targets.Any()) { return null; }
foreach (var target in targets)
{
if (!(target is Character chr)) { continue; }
if (chr.Inventory == null) { continue; }
if (itemTags.Any(tag => chr.Inventory.Items.Any(item => item != null && item.HasTag(tag)))) { return true; }
foreach (var identifier in itemIdentifierSplit)
{
if (chr.Inventory.Items.Any(it => it != null && it.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase)))
{
return true;
}
}
}
return false;
}
public override string ToDebugString()
{
string subActionStr = "";
if (succeeded.HasValue)
{
subActionStr = $"\n Sub action: {(succeeded.Value ? Success : Failure)?.CurrentSubAction.ColorizeObject()}";
}
return $"{ToolBox.GetDebugSymbol(DetermineFinished())} {nameof(CheckItemAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
$"ItemIdentifiers: {ItemIdentifiers.ColorizeObject()}" +
$"Succeeded: {(succeeded.HasValue ? succeeded.Value.ToString() : "not determined").ColorizeObject()})" +
subActionStr;
}
}
}
@@ -0,0 +1,61 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Xml.Linq;
namespace Barotrauma
{
class CheckReputationAction : CheckDataAction
{
[Serialize(ReputationAction.ReputationType.None, true)]
public ReputationAction.ReputationType TargetType { get; set; }
public CheckReputationAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
protected override float GetFloat(CampaignMode campaignMode)
{
switch (TargetType)
{
case ReputationAction.ReputationType.Faction:
{
Faction? faction = campaignMode.Factions.Find(f => f.Prefab.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase));
if (faction != null) { return faction.Reputation.Value; }
break;
}
case ReputationAction.ReputationType.Location:
{
Location? location = campaignMode.Map.CurrentLocation;
Debug.Assert(location?.Reputation != null, "location?.Reputation != null");
if (location?.Reputation != null) { return location.Reputation.Value; }
break;
}
default:
{
DebugConsole.ThrowError("CheckReputationAction requires a \"TargetType\" but none were specified.");
break;
}
}
return 0.0f;
}
protected override bool GetBool(CampaignMode campaignMode)
{
DebugConsole.ThrowError("Boolean comparison cannot be applied to reputations.");
return false;
}
public override string ToDebugString()
{
string condition = "?";
if (value2 != null && value1 != null)
{
condition = $"{value1.ColorizeObject()} {Operator.ColorizeObject()} {value2.ColorizeObject()}";
}
return $"{ToolBox.GetDebugSymbol(succeeded.HasValue)} {nameof(CheckReputationAction)} -> (Type: {TargetType.ColorizeObject()}, " +
$"{(string.IsNullOrWhiteSpace(Identifier) ? string.Empty : $"Identifier: {Identifier.ColorizeObject()}, ")}" +
$"Success: {succeeded.ColorizeObject()}, Expression: {condition})";
}
}
}
@@ -0,0 +1,91 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class CombatAction : EventAction
{
[Serialize(AIObjectiveCombat.CombatMode.Offensive, true)]
public AIObjectiveCombat.CombatMode CombatMode { get; set; }
[Serialize("", true)]
public string NPCTag { get; set; }
[Serialize("", true)]
public string EnemyTag { get; set; }
[Serialize(120.0f, true)]
public float CoolDown { get; set; }
private bool isFinished = false;
private IEnumerable<Character> affectedNpcs = null;
public CombatAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
public override void Update(float deltaTime)
{
if (isFinished) { return; }
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(e => e is Character).Select(e => e as Character);
foreach (var npc in affectedNpcs)
{
if (!(npc.AIController is HumanAIController humanAiController)) { continue; }
Character enemy = null;
float closestDist = float.MaxValue;
foreach (Entity target in ParentEvent.GetTargets(EnemyTag))
{
if (!(target is Character character)) { continue; }
float dist = Vector2.DistanceSquared(npc.WorldPosition, target.WorldPosition);
if (dist < closestDist)
{
enemy = character;
closestDist = dist;
}
}
if (enemy == null) { continue; }
npc.TurnedHostileByEvent = true;
var objectiveManager = humanAiController.ObjectiveManager;
foreach (var goToObjective in objectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
{
goToObjective.Abandon = true;
}
objectiveManager.AddObjective(new AIObjectiveCombat(npc, enemy, CombatMode, objectiveManager, coolDown: CoolDown));
}
isFinished = true;
}
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
if (affectedNpcs != null)
{
foreach (var npc in affectedNpcs)
{
if (npc.Removed || !(npc.AIController is HumanAIController humanAiController)) { continue; }
foreach (var combatObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveCombat>())
{
combatObjective.Abandon = true;
}
}
affectedNpcs = null;
}
isFinished = false;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(CombatAction)} -> (Cooldown: {CoolDown.ColorizeObject()}, CombatMode: {CombatMode.ColorizeObject()}, NPCTag: {NPCTag.ColorizeObject()}, EnemyTag: {EnemyTag.ColorizeObject()})";
}
}
}
@@ -0,0 +1,359 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
partial class ConversationAction : EventAction
{
public enum DialogTypes
{
Regular,
Small,
Mission
}
const float InterruptDistance = 300.0f;
/// <summary>
/// Other events can't trigger conversations if some other event has triggered one within this time.
/// Intended to prevent multiple events from triggering conversations at the same time.
/// </summary>
const float BlockOtherConversationsDuration = 5.0f;
[Serialize("", true)]
public string Text { get; set; }
[Serialize(0, true)]
public int DefaultOption { get; set; }
[Serialize("", true)]
public string SpeakerTag { get; set; }
[Serialize("", true)]
public string TargetTag { get; set; }
[Serialize(true, true)]
public bool WaitForInteraction { get; set; }
[Serialize(false, true)]
public bool FadeToBlack { get; set; }
[Serialize("", true)]
public string EventSprite { get; set; }
[Serialize(DialogTypes.Regular, true)]
public DialogTypes DialogType { get; set; }
[Serialize(false, true)]
public bool ContinueConversation { get; set; }
private Character speaker;
private OrderInfo? prevSpeakerOrder;
public List<SubactionGroup> Options { get; private set; }
public SubactionGroup Interrupted { get; private set; }
private static UInt16 actionCount;
//an identifier the server uses to identify which ConversationAction a client is responding to
public readonly UInt16 Identifier;
private int selectedOption = -1;
private bool dialogOpened = false;
private double lastActiveTime;
private bool interrupt;
public ConversationAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
actionCount++;
Identifier = actionCount;
Options = new List<SubactionGroup>();
foreach (XElement elem in element.Elements())
{
if (elem.Name.LocalName.Equals("option", StringComparison.InvariantCultureIgnoreCase))
{
Options.Add(new SubactionGroup(ParentEvent, elem));
}
else if (elem.Name.LocalName.Equals("interrupt", StringComparison.InvariantCultureIgnoreCase))
{
Interrupted = new SubactionGroup(ParentEvent, elem);
}
}
}
public override IEnumerable<EventAction> GetSubActions()
{
return Options.SelectMany(group => group.Actions);
}
public override bool IsFinished(ref string goTo)
{
if (interrupt)
{
if (dialogOpened)
{
#if CLIENT
dialogBox?.Close();
#else
foreach (Client c in GameMain.Server.ConnectedClients)
{
if (c.InGame && c.Character != null) { ServerWrite(speaker, c); }
}
# endif
ResetSpeaker();
dialogOpened = false;
}
if (Interrupted == null)
{
goTo = "_end";
return true;
}
else
{
return Interrupted.IsFinished(ref goTo);
}
}
if (selectedOption >= 0)
{
if (!Options.Any() || Options[selectedOption].IsFinished(ref goTo))
{
ResetSpeaker();
return true;
}
}
return false;
}
public override void Reset()
{
Options.ForEach(a => a.Reset());
ResetSpeaker();
selectedOption = -1;
interrupt = false;
dialogOpened = false;
speaker = null;
}
public override bool SetGoToTarget(string goTo)
{
selectedOption = -1;
for (int i = 0; i < Options.Count; i++)
{
if (Options[i].SetGoToTarget(goTo))
{
selectedOption = i;
interrupt = false;
dialogOpened = true;
return true;
}
}
return false;
}
private void ResetSpeaker()
{
if (speaker == null) { return; }
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
speaker.SetCustomInteract(null, null);
#if SERVER
GameMain.NetworkMember.CreateEntityEvent(speaker, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
#endif
if (prevSpeakerOrder != null)
{
(speaker.AIController as HumanAIController)?.SetOrder(prevSpeakerOrder.Value.Order, prevSpeakerOrder.Value.OrderOption, orderGiver: null, speak: false);
}
else
{
(speaker.AIController as HumanAIController)?.SetOrder(null, string.Empty, orderGiver: null, speak: false);
}
}
private int[] GetEndingOptions()
{
List<int> endings = Options.Where(group => !group.Actions.Any() || group.EndConversation).Select(group => Options.IndexOf(group)).ToList();
if (!ContinueConversation) { endings.Add(-1); }
return endings.ToArray();
}
public override void Update(float deltaTime)
{
lastActiveTime = Timing.TotalTime;
if (interrupt)
{
Interrupted?.Update(deltaTime);
}
else if (selectedOption < 0)
{
if (dialogOpened)
{
#if CLIENT
Character.DisableControls = true;
#endif
if (ShouldInterrupt())
{
ResetSpeaker();
interrupt = true;
}
return;
}
if (!string.IsNullOrEmpty(SpeakerTag))
{
if (speaker != null && !speaker.Removed && speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk) { return; }
speaker = ParentEvent.GetTargets(SpeakerTag).FirstOrDefault(e => e is Character) as Character;
if (speaker == null || speaker.Removed)
{
return;
}
//some conversation already assigned to the speaker, wait for it to be removed
if (speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk)
{
return;
}
else if (!WaitForInteraction)
{
TryStartConversation(speaker);
}
else
{
speaker.CampaignInteractionType = CampaignMode.InteractionType.Talk;
#if CLIENT
speaker.SetCustomInteract(
TryStartConversation,
TextManager.GetWithVariable("CampaignInteraction.Talk", "[key]", GameMain.Config.KeyBindText(InputType.Use)));
#else
speaker.SetCustomInteract(
TryStartConversation,
TextManager.Get("CampaignInteraction.Talk"));
GameMain.NetworkMember.CreateEntityEvent(speaker, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
#endif
}
return;
}
else
{
TryStartConversation(null);
}
}
else if (Options.Any())
{
Options[selectedOption].Update(deltaTime);
}
}
private bool ShouldInterrupt()
{
IEnumerable<Entity> targets = Enumerable.Empty<Entity>();
if (!string.IsNullOrEmpty(TargetTag))
{
targets = ParentEvent.GetTargets(TargetTag).Where(e => IsValidTarget(e));
if (!targets.Any()) { return true; }
}
if (speaker != null)
{
if (!string.IsNullOrEmpty(TargetTag))
{
if (targets.All(t => Vector2.DistanceSquared(t.WorldPosition, speaker.WorldPosition) > InterruptDistance * InterruptDistance)) { return true; }
}
if (speaker.AIController is HumanAIController humanAI && !humanAI.AllowCampaignInteraction())
{
return true;
}
return speaker.Removed || speaker.IsDead || speaker.IsIncapacitated;
}
return false;
}
private bool IsValidTarget(Entity e)
{
return
e is Character character && !character.Removed && !character.IsDead && !character.IsIncapacitated &&
(e == Character.Controlled || character.IsRemotePlayer);
}
private void TryStartConversation(Character speaker, Character targetCharacter = null)
{
IEnumerable<Entity> targets = Enumerable.Empty<Entity>();
if (!string.IsNullOrEmpty(TargetTag))
{
targets = ParentEvent.GetTargets(TargetTag).Where(e => IsValidTarget(e));
if (!targets.Any() || IsBlockedByAnotherConversation(targets)) { return; }
}
if (speaker?.AIController is HumanAIController humanAI)
{
prevSpeakerOrder = null;
if (humanAI.CurrentOrder != null)
{
prevSpeakerOrder = new OrderInfo(humanAI.CurrentOrder, humanAI.CurrentOrderOption);
}
humanAI.SetOrder(
Order.PrefabList.Find(o => o.Identifier.Equals("wait", StringComparison.OrdinalIgnoreCase)),
option: string.Empty, orderGiver: null, speak: false);
if (targets.Any())
{
Entity closestTarget = null;
float closestDist = float.MaxValue;
foreach (Entity entity in targets)
{
float dist = Vector2.DistanceSquared(entity.WorldPosition, speaker.WorldPosition);
if (dist < closestDist)
{
closestTarget = entity;
closestDist = dist;
}
}
if (closestTarget != null)
{
humanAI.FaceTarget(closestTarget);
}
}
}
ShowDialog(speaker, targetCharacter);
dialogOpened = true;
}
partial void ShowDialog(Character speaker, Character targetCharacter);
public override string ToDebugString()
{
if (!interrupt)
{
SubactionGroup selOtion = null;
if (selectedOption >= 0 && Options.Count > selectedOption)
{
selOtion = Options[selectedOption];
}
EventAction subAction = null;
if (selOtion != null)
{
subAction = selOtion.CurrentSubAction;
}
return $"{ToolBox.GetDebugSymbol(selectedOption > -1)} {nameof(ConversationAction)} -> (Selected option: {selOtion?.Text.ColorizeObject()})\n" +
$" Sub action: {subAction.ColorizeObject()}";
}
else
{
return $"{ToolBox.GetDebugSymbol(true)} {nameof(ConversationAction)} -> (Interrupted)\n" +
$" Sub action: {Interrupted?.CurrentSubAction.ColorizeObject()}";
}
}
}
}
@@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
namespace Barotrauma
{
abstract class EventAction
{
public class SubactionGroup
{
public string Text;
public List<EventAction> Actions;
public bool EndConversation;
private int currentSubAction = 0;
public EventAction CurrentSubAction
{
get
{
if (currentSubAction >= 0 && Actions.Count > currentSubAction)
{
return Actions[currentSubAction];
}
return null;
}
}
public SubactionGroup(ScriptedEvent scriptedEvent, XElement elem)
{
Text = elem.Attribute("text")?.Value ?? "";
Actions = new List<EventAction>();
EndConversation = elem.GetAttributeBool("endconversation", false);
foreach (XElement e in elem.Elements())
{
if (e.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase))
{
DebugConsole.ThrowError($"Error in event prefab \"{scriptedEvent.Prefab.Identifier}\". Status effect configured as a sub action (text: \"{Text}\"). Please configure status effects as child elements of a StatusEffectAction.");
continue;
}
Actions.Add(Instantiate(scriptedEvent, e));
}
}
public bool IsFinished(ref string goTo)
{
if (currentSubAction < Actions.Count)
{
string innerGoTo = null;
if (Actions[currentSubAction].IsFinished(ref innerGoTo))
{
if (string.IsNullOrEmpty(innerGoTo))
{
currentSubAction++;
}
else
{
goTo = innerGoTo;
return true;
}
}
}
if (currentSubAction >= Actions.Count)
{
return true;
}
return false;
}
public bool SetGoToTarget(string goTo)
{
currentSubAction = 0;
for (int i = 0; i < Actions.Count; i++)
{
if (Actions[i].SetGoToTarget(goTo))
{
currentSubAction = i;
return true;
}
}
return false;
}
public void Reset()
{
Actions.ForEach(a => a.Reset());
currentSubAction = 0;
}
public void Update(float deltaTime)
{
if (currentSubAction < Actions.Count)
{
Actions[currentSubAction].Update(deltaTime);
}
}
}
public readonly ScriptedEvent ParentEvent;
public EventAction(ScriptedEvent parentEvent, XElement element)
{
ParentEvent = parentEvent;
SerializableProperty.DeserializeProperties(this, element);
}
/// <summary>
/// Has the action finished.
/// </summary>
/// <param name="goToLabel">If null or empty, the event moves to the next action. Otherwise it moves to the specified label.</param>
/// <returns></returns>
public abstract bool IsFinished(ref string goToLabel);
public virtual bool SetGoToTarget(string goTo)
{
return false;
}
public abstract void Reset();
public virtual bool CanBeFinished()
{
return true;
}
public virtual IEnumerable<EventAction> GetSubActions()
{
return Enumerable.Empty<EventAction>();
}
public virtual void Update(float deltaTime) { }
public static EventAction Instantiate(ScriptedEvent scriptedEvent, XElement element)
{
Type actionType = null;
try
{
actionType = Type.GetType("Barotrauma." + element.Name, true, true);
if (actionType == null) { throw new NullReferenceException(); }
}
catch
{
DebugConsole.ThrowError("Could not find an event class of the type \"" + element.Name + "\".");
return null;
}
ConstructorInfo constructor = actionType.GetConstructor(new[] { typeof(ScriptedEvent), typeof(XElement) });
try
{
return constructor.Invoke(new object[] { scriptedEvent, element }) as EventAction;
}
catch (Exception ex)
{
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString());
return null;
}
}
/// <summary>
/// Rich test to display in debugdraw
/// </summary>
/// <example>
/// <code>
/// public override string ToDebugString()
/// {
/// return $"{ToolBox.GetDebugSymbol(isFinished)} SomeAction -> "(someInfo: {info.ColorizeObject()})";
/// }
/// </code>
/// </example>
/// <returns></returns>
public virtual string ToDebugString()
{
return $"[?] {GetType().Name}";
}
}
}
@@ -0,0 +1,50 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class FireAction : EventAction
{
[Serialize(10.0f, true)]
public float Size { get; set; }
[Serialize("", true)]
public string TargetTag { get; set; }
public FireAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
private bool isFinished = false;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
var targets = ParentEvent.GetTargets(TargetTag);
foreach (var target in targets)
{
Vector2 pos = target.WorldPosition;
var newFire = new FireSource(pos);
newFire.Size = new Vector2(Size, Size);
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(FireAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
$"Size: {Size.ColorizeObject()})";
}
}
}
@@ -0,0 +1,56 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class GiveSkillExpAction : EventAction
{
[Serialize("", true)]
public string Skill { get; set; }
[Serialize(0.0f, true)]
public float Amount { get; set; }
[Serialize("", true)]
public string TargetTag { get; set; }
public GiveSkillExpAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
if (string.IsNullOrEmpty(TargetTag))
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": GiveSkillExpAction without a target tag (the action needs to know whose skill to check).");
}
}
private bool isFinished = false;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
var targets = ParentEvent.GetTargets(TargetTag).Where(e => e is Character).Select(e => e as Character);
foreach (var target in targets)
{
target.Info?.IncreaseSkillLevel(Skill, Amount, target.WorldPosition + Vector2.UnitY * 150.0f);
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(GiveSkillExpAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
$"Skill: {Skill.ColorizeObject()}, Amount: {Amount.ColorizeObject()})";
}
}
}
@@ -0,0 +1,25 @@
using System.Xml.Linq;
namespace Barotrauma
{
class GoTo : EventAction
{
[Serialize("", true)]
public string Name { get; set; }
public GoTo(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
public override bool IsFinished(ref string goTo)
{
goTo = Name;
return true;
}
public override string ToDebugString()
{
return $"[-] Go to label \"{Name}\"";
}
public override void Reset() { }
}
}
@@ -0,0 +1,29 @@
using System.Xml.Linq;
namespace Barotrauma
{
class Label : EventAction
{
[Serialize("", true)]
public string Name { get; set; }
public Label(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
public override bool IsFinished(ref string goTo)
{
return true;
}
public override bool SetGoToTarget(string goTo)
{
return goTo.Equals(Name, System.StringComparison.InvariantCultureIgnoreCase);
}
public override string ToDebugString()
{
return $"[-] Label \"{Name}\"";
}
public override void Reset() { }
}
}
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
class MissionAction : EventAction
{
[Serialize("", true)]
public string MissionIdentifier { get; set; }
[Serialize("", true)]
public string MissionTag { get; set; }
private bool isFinished;
public MissionAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
//TODO: use event identifier in the error messages
if (string.IsNullOrEmpty(MissionIdentifier) && string.IsNullOrEmpty(MissionTag))
{
DebugConsole.ThrowError($"Error in event \"{"event identifier goes here"}\": neither MissionIdentifier or MissionTag has been configured.");
}
if (!string.IsNullOrEmpty(MissionIdentifier) && !string.IsNullOrEmpty(MissionTag))
{
DebugConsole.ThrowError($"Error in event \"{"event identifier goes here"}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored.");
}
}
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
if (GameMain.GameSession.GameMode is CampaignMode campaign)
{
MissionPrefab prefab = null;
if (!string.IsNullOrEmpty(MissionIdentifier))
{
prefab = campaign.Map.CurrentLocation.UnlockMissionByIdentifier(MissionIdentifier);
}
else if (!string.IsNullOrEmpty(MissionTag))
{
prefab = campaign.Map.CurrentLocation.UnlockMissionByTag(MissionTag);
}
if (campaign is MultiPlayerCampaign mpCampaign)
{
mpCampaign.LastUpdateID++;
}
if (prefab != null)
{
#if CLIENT
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", prefab.Name),
new string[0], type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
{
IconColor = prefab.IconColor
};
#else
NotifyMissionUnlock(prefab);
#endif
}
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(MissionAction)} -> ({(string.IsNullOrEmpty(MissionIdentifier) ? MissionTag : MissionIdentifier)})";
}
#if SERVER
private void NotifyMissionUnlock(MissionPrefab prefab)
{
foreach (Client client in GameMain.Server.ConnectedClients)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte) ServerPacketHeader.EVENTACTION);
outmsg.Write((byte) EventManager.NetworkEventType.MISSION);
outmsg.Write(prefab.Identifier);
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
}
}
#endif
}
}
@@ -0,0 +1,44 @@
using System;
using System.Xml.Linq;
namespace Barotrauma
{
class MoneyAction : EventAction
{
public MoneyAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
[Serialize(0, true)]
public int Amount { get; set; }
private bool isFinished;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
campaign.Money += Amount;
#if SERVER
(campaign as MultiPlayerCampaign).LastUpdateID++;
#endif
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(SetDataAction)} -> (Amount: {Amount.ColorizeObject()})";
}
}
}
@@ -0,0 +1,94 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class NPCFollowAction : EventAction
{
[Serialize("", true)]
public string NPCTag { get; set; }
[Serialize("", true)]
public string TargetTag { get; set; }
[Serialize(true, true)]
public bool Follow { get; set; }
private bool isFinished = false;
public NPCFollowAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
private List<Character> affectedNpcs = null;
private Entity target = null;
public override void Update(float deltaTime)
{
if (isFinished) { return; }
target = ParentEvent.GetTargets(TargetTag).FirstOrDefault();
if (target == null) { return; }
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
foreach (var npc in affectedNpcs)
{
if (!(npc.AIController is HumanAIController humanAiController)) { continue; }
if (Follow)
{
var newObjective = new AIObjectiveGoTo(target, npc, humanAiController.ObjectiveManager, repeat: true)
{
OverridePriority = 100.0f
};
humanAiController.ObjectiveManager.AddObjective(newObjective);
humanAiController.ObjectiveManager.WaitTimer = 0.0f;
}
else
{
foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
{
if (goToObjective.Target == target)
{
goToObjective.Abandon = true;
}
}
}
}
isFinished = true;
}
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
if (affectedNpcs != null && target != null)
{
foreach (var npc in affectedNpcs)
{
if (npc.Removed || !(npc.AIController is HumanAIController humanAiController)) { continue; }
foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
{
if (goToObjective.Target == target)
{
goToObjective.Abandon = true;
}
}
}
target = null;
affectedNpcs = null;
}
isFinished = false;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(NPCFollowAction)} -> (NPCTag: {NPCTag.ColorizeObject()}, TargetTag: {TargetTag.ColorizeObject()}, Follow: {Follow.ColorizeObject()})";
}
}
}
@@ -0,0 +1,85 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class NPCWaitAction : EventAction
{
[Serialize("", true)]
public string NPCTag { get; set; }
[Serialize(true, true)]
public bool Wait { get; set; }
private bool isFinished = false;
public NPCWaitAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
private List<Character> affectedNpcs = null;
public override void Update(float deltaTime)
{
if (isFinished) { return; }
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
foreach (var npc in affectedNpcs)
{
if (!(npc.AIController is HumanAIController humanAiController)) { continue; }
if (Wait)
{
var newObjective = new AIObjectiveGoTo(npc, npc, humanAiController.ObjectiveManager, repeat: true)
{
OverridePriority = 100.0f
};
humanAiController.ObjectiveManager.AddObjective(newObjective);
humanAiController.ObjectiveManager.WaitTimer = 0.0f;
}
else
{
foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
{
if (goToObjective.Target == npc)
{
goToObjective.Abandon = true;
}
}
}
}
isFinished = true;
}
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
if (affectedNpcs != null)
{
foreach (var npc in affectedNpcs)
{
if (npc.Removed || !(npc.AIController is HumanAIController humanAiController)) { continue; }
foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
{
if (goToObjective.Target == npc)
{
goToObjective.Abandon = true;
}
}
}
affectedNpcs = null;
}
isFinished = false;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(NPCWaitAction)} -> (NPCTag: {NPCTag.ColorizeObject()}, Wait: {Wait.ColorizeObject()})";
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma
{
class RNGAction : BinaryOptionAction
{
[Serialize(0.0f, true)]
public float Chance { get; set; }
public RNGAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
protected override bool? DetermineSuccess()
{
return Rand.Range(0.0, 1.0) <= Chance;
}
public override string ToDebugString()
{
string subActionStr = "";
if (succeeded.HasValue)
{
subActionStr = $"\n Sub action: {(succeeded.Value ? Success : Failure)?.CurrentSubAction.ColorizeObject()}";
}
return $"{ToolBox.GetDebugSymbol(DetermineFinished())} {nameof(RNGAction)} -> (Chance: {Chance.ColorizeObject()}, "+
$"Succeeded: {(succeeded.HasValue ? succeeded.Value.ToString() : "not determined").ColorizeObject()})" +
subActionStr;
}
}
}
@@ -0,0 +1,58 @@
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class RemoveItemAction : EventAction
{
[Serialize("", true)]
public string TargetTag { get; set; }
[Serialize("", true)]
public string ItemIdentifier { get; set; }
[Serialize(1, true)]
public int Amount { get; set; }
public RemoveItemAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
private bool isFinished = false;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
var targets = ParentEvent.GetTargets(TargetTag)
.Where(t => t is Character chr && chr.Inventory != null)
.Select(t => t as Character).ToList();
if (targets.Count <= 0) { return; }
int count = Amount;
while (count > 0 && targets.Count > 0)
{
var items = targets[0].Inventory.Items;
for (int i = 0; i < items.Length; i++)
{
if (items[i] != null && items[i].Prefab.Identifier.Equals(ItemIdentifier, StringComparison.InvariantCultureIgnoreCase))
{
Entity.Spawner.AddToRemoveQueue(items[i]);
count--;
if (count <= 0) { break; }
}
}
targets.RemoveAt(0);
}
isFinished = true;
}
}
}
@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class ReputationAction : EventAction
{
public enum ReputationType
{
None,
Location,
Faction
}
public ReputationAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
[Serialize(0.0f, true)]
public float Increase { get; set; }
[Serialize("", true)]
public string Identifier { get; set; }
[Serialize(ReputationType.None, true)]
public ReputationType TargetType { get; set; }
private bool isFinished;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
switch (TargetType)
{
case ReputationType.Faction:
{
Faction faction = campaign.Factions.Find(faction1 => faction1.Prefab.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase));
if (faction != null)
{
faction.Reputation.Value += Increase;
}
else
{
DebugConsole.ThrowError($"Faction with the identifier \"{Identifier}\" was not found.");
}
break;
}
case ReputationType.Location:
{
Location location = campaign.Map.CurrentLocation;
if (location != null)
{
location.Reputation.Value += Increase;
IEnumerable<Location> locations = location.Connections.SelectMany(c => c.Locations).Distinct().Where(l => l != null && l != location);
foreach (Location connectedLocation in locations)
{
Debug.Assert(connectedLocation.Reputation != null, "connectedLocation.Reputation != null");
if (connectedLocation.Reputation != null)
{
connectedLocation.Reputation.Value += (Increase / 4);
}
}
}
break;
}
default:
{
DebugConsole.ThrowError("ReputationAction requires a \"TargetType\" but none were specified.");
break;
}
}
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(ReputationAction)} -> (FactionIdentifier: {Identifier.ColorizeObject()}, TargetType: {TargetType.ColorizeObject()}, Increase: {Increase.ColorizeObject()})";
}
}
}
@@ -0,0 +1,105 @@
using System;
using System.Xml.Linq;
namespace Barotrauma
{
class SetDataAction : EventAction
{
public enum OperationType
{
Set,
Multiply,
Add
}
public SetDataAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
[Serialize(OperationType.Set, true)]
public OperationType Operation { get; set; }
[Serialize(null, true)]
public string Value { get; set; } = null!;
[Serialize("", true)]
public string Identifier { get; set; }
private bool isFinished;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{
object currentValue = campaign.CampaignMetadata.GetValue(Identifier);
object xmlValue = ConvertXMLValue();
float? originalValue = ConvertValueToFloat(currentValue ?? 0);
float? newValue = ConvertValueToFloat(xmlValue);
if ((originalValue == null || newValue == null) && Operation != OperationType.Set)
{
DebugConsole.ThrowError($"Tried to perform numeric operations to a non number via SetDataAction (Existing: {currentValue?.GetType()}, New: {xmlValue.GetType()})");
return;
}
if (Identifier != null)
{
switch (Operation)
{
case OperationType.Set:
campaign.CampaignMetadata.SetValue(Identifier, xmlValue);
break;
case OperationType.Add:
campaign.CampaignMetadata.SetValue(Identifier, originalValue + newValue ?? 0);
break;
case OperationType.Multiply:
campaign.CampaignMetadata.SetValue(Identifier, originalValue * newValue ?? 0);
break;
}
}
}
isFinished = true;
}
private static float? ConvertValueToFloat(object value)
{
if (value is float || value is int)
{
return (float?) Convert.ChangeType(value, typeof(float));
}
return null;
}
private object ConvertXMLValue()
{
if (bool.TryParse(Value, out bool b))
{
return b;
}
if (float.TryParse(Value, out float f))
{
return f;
}
return Value;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(SetDataAction)} -> (Identifier: {Identifier.ColorizeObject()}, Value: {ConvertXMLValue().ColorizeObject()}, Operation: {Operation.ColorizeObject()})";
}
}
}
@@ -0,0 +1,105 @@
using System;
using System.Xml.Linq;
namespace Barotrauma
{
class SetPriceMultiplierAction : EventAction
{
public enum OperationType
{
Set,
Multiply,
Min,
Max
}
public enum PriceMultiplierType
{
Store,
Mechanical
}
[Serialize(1.0f, true)]
public float Multiplier { get; set; }
[Serialize(OperationType.Set, true)]
public OperationType Operation { get; set; }
[Serialize(PriceMultiplierType.Store, true)]
public PriceMultiplierType TargetMultiplier { get; set; }
public SetPriceMultiplierAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
private bool isFinished = false;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
if (GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.Map?.CurrentLocation != null)
{
float newMultiplier = GetCurrentMultiplier(campaign.Map.CurrentLocation);
switch (Operation)
{
case OperationType.Set:
newMultiplier = Multiplier;
break;
case OperationType.Multiply:
newMultiplier *= Multiplier;
break;
case OperationType.Min:
newMultiplier = Math.Min(Multiplier, campaign.Map.CurrentLocation.PriceMultiplier);
break;
case OperationType.Max:
newMultiplier = Math.Max(Multiplier, campaign.Map.CurrentLocation.PriceMultiplier);
break;
default:
throw new NotImplementedException();
}
SetCurrentMultiplier(campaign.Map.CurrentLocation, newMultiplier);
}
isFinished = true;
}
private float GetCurrentMultiplier(Location location)
{
return TargetMultiplier switch
{
PriceMultiplierType.Store => location.PriceMultiplier,
PriceMultiplierType.Mechanical => location.MechanicalPriceMultiplier,
_ => throw new NotImplementedException()
};
}
private void SetCurrentMultiplier(Location location, float value)
{
switch (TargetMultiplier)
{
case PriceMultiplierType.Store:
location.PriceMultiplier = value;
break;
case PriceMultiplierType.Mechanical:
location.MechanicalPriceMultiplier = value;
break;
default:
throw new NotImplementedException();
}
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(SetPriceMultiplierAction)} -> (Multiplier: {Multiplier.ColorizeObject()}, " +
$"Operation: {Operation.ColorizeObject()}, Target: {TargetMultiplier})";
}
}
}
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class SkillCheckAction : BinaryOptionAction
{
[Serialize("", true)]
public string RequiredSkill { get; set; }
[Serialize(0.0f, true)]
public float RequiredLevel { get; set; }
[Serialize("", true)]
public string TargetTag { get; set; }
public SkillCheckAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
if (string.IsNullOrEmpty(TargetTag))
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": SkillCheckAction without a target tag (the action needs to know whose skill to check).");
}
}
protected override bool? DetermineSuccess()
{
var potentialTargets = ParentEvent.GetTargets(TargetTag).Where(e => e is Character).Select(e => e as Character);
return potentialTargets.Any(chr => chr.GetSkillLevel(RequiredSkill?.ToLowerInvariant()) >= RequiredLevel);
}
public override string ToDebugString()
{
string subActionStr = "";
if (succeeded.HasValue)
{
subActionStr = $"\n Sub action: {(succeeded.Value ? Success : Failure)?.CurrentSubAction.ColorizeObject()}";
}
return $"{ToolBox.GetDebugSymbol(DetermineFinished())} {nameof(SkillCheckAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
$"Required skill: {RequiredSkill.ColorizeObject()}, Required level: {RequiredLevel.ColorizeObject()}, " +
$"Succeeded: {(succeeded.HasValue ? succeeded.Value.ToString() : "not determined").ColorizeObject()})" +
subActionStr;
}
}
}
@@ -0,0 +1,335 @@
using System;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class SpawnAction : EventAction
{
public enum SpawnLocationType
{
MainSub,
Outpost,
MainPath,
Ruin,
Wreck
}
[Serialize("", true, description: "Species name of the character to spawn.")]
public string SpeciesName { get; set; }
[Serialize("", true, description: "Identifier of the NPC set to choose from.")]
public string NPCSetIdentifier { get; set; }
[Serialize("", true, description: "Identifier of the NPC.")]
public string NPCIdentifier { get; set; }
[Serialize(true, true, description: "Should taking the items of this npc be considered as stealing?")]
public bool LootingIsStealing { get; set; }
[Serialize("", true, description: "Identifier of the item to spawn.")]
public string ItemIdentifier { get; set; }
[Serialize("", true, description: "The spawned entity will be assigned this tag. The tag can be used to refer to the entity by other actions of the event.")]
public string TargetTag { get; set; }
[Serialize("", true, description: "Tag of an entity with an inventory to spawn the item into.")]
public string TargetInventory { get; set; }
[Serialize(SpawnLocationType.MainSub, true)]
public SpawnLocationType SpawnLocation { get; set; }
[Serialize(SpawnType.Human, true)]
public SpawnType SpawnPointType { get; set; }
[Serialize("", true)]
public string SpawnPointTag { get; set; }
private readonly HashSet<string> targetModuleTags = new HashSet<string>();
[Serialize("", true, "What outpost module tags does the entity prefer to spawn in.")]
public string TargetModuleTags
{
get => string.Join(",", targetModuleTags);
set
{
targetModuleTags.Clear();
if (!string.IsNullOrWhiteSpace(value))
{
string[] splitTags = value.Split(',');
foreach (var s in splitTags)
{
targetModuleTags.Add(s);
}
}
}
}
private bool spawned;
private Entity spawnedEntity;
private readonly bool ignoreSpawnPointType;
public SpawnAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
ignoreSpawnPointType = !element.Attributes().Any(a => a.Name.ToString().Equals("spawnpointtype", StringComparison.OrdinalIgnoreCase));
}
public override bool IsFinished(ref string goTo)
{
if (spawnedEntity != null)
{
return true;
}
else
{
return false;
}
}
public override void Reset()
{
spawned = false;
spawnedEntity = null;
}
public override void Update(float deltaTime)
{
if (spawned) { return; }
if (!string.IsNullOrEmpty(NPCSetIdentifier) && !string.IsNullOrEmpty(NPCIdentifier))
{
HumanPrefab humanPrefab = NPCSet.Get(NPCSetIdentifier, NPCIdentifier);
ISpatialEntity spawnPos = GetSpawnPos();
Entity.Spawner.AddToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos?.WorldPosition ?? Vector2.Zero, 100.0f), onSpawn: newCharacter =>
{
newCharacter.TeamID = Character.TeamType.FriendlyNPC;
newCharacter.EnableDespawn = false;
humanPrefab.GiveItems(newCharacter, newCharacter.Submarine);
if (LootingIsStealing)
{
foreach (Item item in newCharacter.Inventory.Items)
{
if (item != null) { item.SpawnedInOutpost = true; }
}
}
newCharacter.CharacterHealth.MaxVitality *= humanPrefab.HealthMultiplier;
var humanAI = newCharacter.AIController as HumanAIController;
if (humanAI != null)
{
var idleObjective = humanAI.ObjectiveManager.GetObjective<AIObjectiveIdle>();
if (idleObjective != null)
{
idleObjective.Behavior = humanPrefab.BehaviorType;
foreach (string moduleType in humanPrefab.PreferredOutpostModuleTypes)
{
idleObjective.PreferredOutpostModuleTypes.Add(moduleType);
}
}
}
if (humanPrefab.CampaignInteractionType != CampaignMode.InteractionType.None)
{
(GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(newCharacter, humanPrefab.CampaignInteractionType);
if (spawnPos != null && humanAI != null)
{
humanAI.ObjectiveManager.SetOrder(new AIObjectiveGoTo(spawnPos, newCharacter, humanAI.ObjectiveManager, repeat: true, getDivingGearIfNeeded: false, closeEnough: 200));
}
}
if (!string.IsNullOrEmpty(TargetTag) && newCharacter != null)
{
ParentEvent.AddTarget(TargetTag, newCharacter);
}
spawnedEntity = newCharacter;
});
}
else if (!string.IsNullOrEmpty(SpeciesName))
{
Entity.Spawner.AddToSpawnQueue(SpeciesName, OffsetSpawnPos(GetSpawnPos()?.WorldPosition ?? Vector2.Zero, 100.0f), onSpawn: newCharacter =>
{
if (!string.IsNullOrEmpty(TargetTag) && newCharacter != null)
{
ParentEvent.AddTarget(TargetTag, newCharacter);
}
spawnedEntity = newCharacter;
});
}
else if (!string.IsNullOrEmpty(ItemIdentifier))
{
if (!(MapEntityPrefab.Find(null, identifier: ItemIdentifier) is ItemPrefab itemPrefab))
{
DebugConsole.ThrowError("Error in SpawnAction (item prefab \"" + ItemIdentifier + "\" not found)");
}
else
{
Inventory spawnInventory = null;
if (!string.IsNullOrEmpty(TargetInventory))
{
var targets = ParentEvent.GetTargets(TargetInventory);
if (targets.Any())
{
var target = targets.First(t => t is Item || t is Character);
if (target is Character character)
{
spawnInventory = character.Inventory;
}
else if (target is Item item)
{
spawnInventory = item.OwnInventory;
}
}
if (spawnInventory == null)
{
DebugConsole.ThrowError($"Could not spawn \"{ItemIdentifier}\" in target inventory \"{TargetInventory}\"");
}
}
if (spawnInventory == null)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, OffsetSpawnPos(GetSpawnPos()?.WorldPosition ?? Vector2.Zero, 100.0f), onSpawned: onSpawned);
}
else
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, spawnInventory, onSpawned: onSpawned);
}
void onSpawned(Item newItem)
{
if (!string.IsNullOrEmpty(TargetTag) && newItem != null)
{
ParentEvent.AddTarget(TargetTag, newItem);
}
spawnedEntity = newItem;
}
}
}
spawned = true;
}
public static Vector2 OffsetSpawnPos(Vector2 pos, float offsetAmount)
{
Hull hull = Hull.FindHull(pos);
pos += Rand.Vector(offsetAmount);
if (hull != null)
{
float margin = 50.0f;
pos = new Vector2(
MathHelper.Clamp(pos.X, hull.WorldRect.X + margin, hull.WorldRect.Right - margin),
MathHelper.Clamp(pos.Y, hull.WorldRect.Y - hull.WorldRect.Height + margin, hull.WorldRect.Y - margin));
}
return pos;
}
private ISpatialEntity GetSpawnPos()
{
if (!string.IsNullOrWhiteSpace(SpawnPointTag))
{
List<Item> potentialItems = SpawnLocation switch
{
SpawnLocationType.MainSub => Item.ItemList.FindAll(it => it.Submarine == Submarine.MainSub),
SpawnLocationType.MainPath => Item.ItemList.FindAll(it => it.Submarine == null && it.ParentRuin == null),
SpawnLocationType.Outpost => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsOutpost),
SpawnLocationType.Wreck => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsWreck),
SpawnLocationType.Ruin => Item.ItemList.FindAll(it => it.ParentRuin != null),
_ => throw new NotImplementedException()
};
var item = potentialItems.Where(it => it.HasTag(SpawnPointTag)).GetRandom();
if (item != null) { return item; }
var target = ParentEvent.GetTargets(SpawnPointTag).GetRandom();
if (target != null) { return target; }
}
SpawnType? spawnPointType = null;
if (!ignoreSpawnPointType) { spawnPointType = SpawnPointType; }
return GetSpawnPos(SpawnLocation, spawnPointType, targetModuleTags, SpawnPointTag.ToEnumerable());
}
public static WayPoint GetSpawnPos(SpawnLocationType spawnLocation, SpawnType? spawnPointType, IEnumerable<string> moduleFlags = null, IEnumerable<string> spawnpointTags = null)
{
List<WayPoint> potentialSpawnPoints = spawnLocation switch
{
SpawnLocationType.MainSub => WayPoint.WayPointList.FindAll(wp => wp.Submarine == Submarine.MainSub && wp.CurrentHull != null),
SpawnLocationType.MainPath => WayPoint.WayPointList.FindAll(wp => wp.Submarine == null && wp.ParentRuin == null),
SpawnLocationType.Outpost => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.CurrentHull != null && wp.Submarine.Info.IsOutpost),
SpawnLocationType.Wreck => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.Submarine.Info.IsWreck),
SpawnLocationType.Ruin => WayPoint.WayPointList.FindAll(wp => wp.ParentRuin != null),
_ => throw new NotImplementedException()
};
potentialSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.ConnectedDoor == null && wp.Ladders == null && !wp.isObstructed);
if (moduleFlags != null && moduleFlags.Any())
{
List<WayPoint> spawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Any(moduleFlags.Contains) ?? false).ToList();
if (spawnPoints.Any())
{
potentialSpawnPoints = spawnPoints;
}
}
if (spawnpointTags != null && spawnpointTags.Any())
{
var spawnPoints = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag)))
.Where(wp => wp.ConnectedDoor == null && !wp.isObstructed);
if (spawnPoints.Any())
{
potentialSpawnPoints = spawnPoints.ToList();
}
}
if (potentialSpawnPoints.Count == 0)
{
DebugConsole.ThrowError($"Could not find a spawn point for a SpawnAction (spawn location: {spawnLocation})");
return null;
}
IEnumerable<WayPoint> validSpawnPoints;
if (spawnPointType.HasValue)
{
validSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.SpawnType == spawnPointType.Value);
}
else
{
validSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.SpawnType != SpawnType.Path);
if (!validSpawnPoints.Any()) { validSpawnPoints = potentialSpawnPoints; }
}
//don't spawn in an airlock module if there are other options
var airlockSpawnPoints = validSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false);
if (airlockSpawnPoints.Count() < validSpawnPoints.Count())
{
validSpawnPoints = validSpawnPoints.Except(airlockSpawnPoints);
}
if (!validSpawnPoints.Any())
{
DebugConsole.ThrowError($"Could not find a spawn point of the correct type for a SpawnAction (spawn location: {spawnLocation}, type: {spawnPointType}, module flags: {((moduleFlags == null || !moduleFlags.Any()) ? "none" : string.Join(", ", moduleFlags))})");
return potentialSpawnPoints.GetRandom();
}
//if not trying to spawn at a tagged spawnpoint, favor spawnpoints without tags
if (spawnpointTags == null || !spawnpointTags.Any())
{
var spawnPoints = validSpawnPoints.Where(wp => !wp.Tags.Any());
if (spawnPoints.Any())
{
validSpawnPoints = spawnPoints.ToList();
}
}
return validSpawnPoints.GetRandom();
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(spawned)} {nameof(SpawnAction)} -> (Spawned entity: {spawnedEntity.ColorizeObject()})";
}
}
}
@@ -0,0 +1,68 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma
{
partial class StatusEffectAction : EventAction
{
private readonly List<StatusEffect> effects = new List<StatusEffect>();
private int actionIndex;
[Serialize("", true)]
public string TargetTag { get; set; }
public StatusEffectAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
actionIndex = 0;
foreach (XElement subElement in parentEvent.Prefab.ConfigElement.Descendants())
{
if (subElement == element) { break; }
actionIndex++;
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "statuseffect":
effects.Add(StatusEffect.Load(subElement, $"{nameof(StatusEffectAction)} ({parentEvent.Prefab.Identifier})"));
break;
}
}
}
private bool isFinished = false;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
var targets = ParentEvent.GetTargets(TargetTag);
foreach (StatusEffect effect in effects)
{
foreach (var target in targets)
{
effect.Apply(effect.type, deltaTime, target, target as ISerializableEntity);
}
}
#if SERVER
ServerWrite(targets);
#endif
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(StatusEffectAction)} -> (TargetTag: {TargetTag.ColorizeObject()}";
}
}
}
@@ -0,0 +1,102 @@
using Barotrauma.Extensions;
using System;
using System.Xml.Linq;
namespace Barotrauma
{
class TagAction : EventAction
{
[Serialize("", true)]
public string Criteria { get; set; }
[Serialize("", true)]
public string Tag { get; set; }
private bool isFinished = false;
public TagAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
private void TagPlayers()
{
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsPlayer);
}
private void TagBots()
{
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot);
}
private void TagCrew()
{
#if CLIENT
GameMain.GameSession.CrewManager.GetCharacters().ForEach(c => ParentEvent.AddTarget(Tag, c));
#else
TagPlayers(); TagBots(); //TODO: this seems like it would tag more than it should, fix
#endif
}
private void TagStructuresByIdentifier(string identifier)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && s.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
}
private void TagItemsByIdentifier(string identifier)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && it.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
}
private void TagItemsByTag(string tag)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && it.HasTag(tag));
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
string[] criteriaSplit = Criteria.Split(';');
foreach (string entry in criteriaSplit)
{
string[] kvp = entry.Split(':');
switch (kvp[0].Trim().ToLowerInvariant())
{
case "player":
TagPlayers();
break;
case "bot":
TagBots();
break;
case "crew":
TagCrew();
break;
case "structureidentifier":
if (kvp.Length > 1) { TagStructuresByIdentifier(kvp[1].Trim()); }
break;
case "itemidentifier":
if (kvp.Length > 1) { TagItemsByIdentifier(kvp[1].Trim()); }
break;
case "itemtag":
if (kvp.Length > 1) { TagItemsByTag(kvp[1].Trim()); }
break;
}
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()})";
}
}
}
@@ -0,0 +1,173 @@
using Microsoft.Xna.Framework;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class TriggerAction : EventAction
{
[Serialize("", true, description: "Tag of the first entity that will be used for trigger checks.")]
public string Target1Tag { get; set; }
[Serialize("", true, description: "Tag of the second entity that will be used for trigger checks.")]
public string Target2Tag { get; set; }
[Serialize("", true, description: "If set, the first target has to be within an outpost module of this type.")]
public string TargetModuleType { get; set; }
[Serialize("", true, description: "Tag to apply to the first entity when the trigger check succeeds.")]
public string ApplyToTarget1 { get; set; }
[Serialize("", true, description: "Tag to apply to the second entity when the trigger check succeeds.")]
public string ApplyToTarget2 { get; set; }
[Serialize(0.0f, true, description: "Range both entities must be within to activate the trigger.")]
public float Radius { get; set; }
[Serialize(true, true, description: "If true, characters who are being targeted by some enemy cannot trigger the event.")]
public bool DisableInCombat { get; set; }
private float distance;
public TriggerAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
TargetModuleType = TargetModuleType?.ToLowerInvariant();
}
private bool isFinished = false;
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
var targets1 = ParentEvent.GetTargets(Target1Tag);
if (!targets1.Any()) { return; }
foreach (Entity e1 in targets1)
{
if (DisableInCombat && IsInCombat(e1)) { continue; }
if (!string.IsNullOrEmpty(TargetModuleType))
{
if (IsCloseEnoughToHull(e1, out Hull hull))
{
Trigger(e1, hull);
return;
}
continue;
}
var targets2 = ParentEvent.GetTargets(Target2Tag);
foreach (Entity e2 in targets2)
{
if (e1 == e2) { continue; }
if (DisableInCombat && IsInCombat(e2)) { continue; }
Vector2 pos1 = e1.WorldPosition;
Vector2 pos2 = e2.WorldPosition;
distance = Vector2.Distance(pos1, pos2);
if (((e1 is MapEntity m1) && Submarine.RectContains(m1.WorldRect, pos2)) ||
((e2 is MapEntity m2) && Submarine.RectContains(m2.WorldRect, pos1)) ||
Vector2.DistanceSquared(pos1, pos2) < Radius * Radius)
{
Trigger(e1, e2);
return;
}
}
}
}
private bool IsCloseEnoughToHull(Entity e, out Hull hull)
{
hull = null;
if (Radius <= 0)
{
if (e is Character character && character.CurrentHull != null && character.CurrentHull.OutpostModuleTags.Contains(TargetModuleType))
{
hull = character.CurrentHull;
return true;
}
else if (e is Item item && item.CurrentHull != null && item.CurrentHull.OutpostModuleTags.Contains(TargetModuleType))
{
hull = item.CurrentHull;
return true;
}
return false;
}
else
{
foreach (Hull potentialHull in Hull.hullList)
{
if (!potentialHull.OutpostModuleTags.Contains(TargetModuleType)) { continue; }
Rectangle hullRect = potentialHull.WorldRect;
hullRect.Inflate(Radius, Radius);
if (Submarine.RectContains(hullRect, e.WorldPosition))
{
hull = potentialHull;
return true;
}
}
return false;
}
}
private bool IsInCombat(Entity entity)
{
if (!(entity is Character character)) { return false; }
foreach (Character c in Character.CharacterList)
{
if (c.IsDead || c.Removed || c.IsIncapacitated || !c.Enabled) { continue; }
if (c.IsBot && c.AIController is HumanAIController humanAi)
{
if (humanAi.ObjectiveManager.CurrentObjective is AIObjectiveCombat combatObjective &&
combatObjective.Enemy == character)
{
return true;
}
}
else if (c.AIController is EnemyAIController enemyAI && (enemyAI.State == AIState.Aggressive || enemyAI.State == AIState.Attack))
{
if (enemyAI.SelectedAiTarget?.Entity == character || c.CurrentHull == character.CurrentHull)
{
return true;
}
}
}
return false;
}
private void Trigger(Entity entity1, Entity entity2)
{
if (!string.IsNullOrEmpty(ApplyToTarget1))
{
ParentEvent.AddTarget(ApplyToTarget1, entity1);
}
if (!string.IsNullOrEmpty(ApplyToTarget2))
{
ParentEvent.AddTarget(ApplyToTarget2, entity2);
}
isFinished = true;
}
public override string ToDebugString()
{
if (string.IsNullOrEmpty(TargetModuleType))
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TriggerAction)} -> (Distance: {((int)distance).ColorizeObject()}, Radius: {Radius.ColorizeObject()}, TargetTags: {Target1Tag.ColorizeObject()}, {Target2Tag.ColorizeObject()})";
}
else
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TriggerAction)} -> (TargetTags: {Target1Tag.ColorizeObject()}, {TargetModuleType.ColorizeObject()})";
}
}
}
}
@@ -0,0 +1,48 @@
using System.Xml.Linq;
namespace Barotrauma
{
class TriggerEventAction : EventAction
{
[Serialize("", true)]
public string Identifier { get; set; }
private bool isFinished;
public TriggerEventAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
public override bool IsFinished(ref string goTo)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
public override void Update(float deltaTime)
{
if (isFinished) { return; }
if (GameMain.GameSession?.EventManager != null)
{
var eventPrefab = EventSet.GetEventPrefab(Identifier);
if (eventPrefab == null)
{
DebugConsole.ThrowError($"Error in TriggerEventAction - could not find an event with the identifier {Identifier}.");
}
else
{
GameMain.GameSession.EventManager.QueuedEvents.Enqueue(eventPrefab.CreateInstance());
}
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TriggerEventAction)} -> (EventPrefab: {Identifier.ColorizeObject()})";
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.Xml.Linq;
namespace Barotrauma
{
class WaitAction : EventAction
{
[Serialize(0.0f, true)]
public float Time { get; set; }
private float timeRemaining;
public WaitAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
{
timeRemaining = Time;
}
public override bool IsFinished(ref string goTo)
{
return timeRemaining <= 0;
}
public override void Reset()
{
timeRemaining = Time;
}
public override void Update(float deltaTime)
{
timeRemaining -= deltaTime;
if (timeRemaining < 0.0f) { timeRemaining = 0.0f; }
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(timeRemaining <= 0)} {nameof(WaitAction)} -> (Remaining: {timeRemaining.ColorizeObject()}, Time: {Time.ColorizeObject()})";
}
}
}