v1.6.17.0 (Unto the Breach update)
This commit is contained in:
@@ -45,7 +45,6 @@ namespace Barotrauma
|
||||
foreach (ContentXElement subElement in conditionalElements)
|
||||
{
|
||||
conditionalList.AddRange(PropertyConditional.FromXElement(subElement));
|
||||
break;
|
||||
}
|
||||
Conditionals = conditionalList.ToImmutableArray();
|
||||
}
|
||||
|
||||
@@ -200,6 +200,10 @@ namespace Barotrauma
|
||||
{
|
||||
condition = $"{value1.ColorizeObject()} {Operator.ColorizeObject()} {value2.ColorizeObject()}";
|
||||
}
|
||||
else if (!Identifier.IsEmpty)
|
||||
{
|
||||
condition = $"{Identifier} {Condition}".ColorizeObject();
|
||||
}
|
||||
|
||||
return $"{ToolBox.GetDebugSymbol(succeeded.HasValue)} {nameof(CheckDataAction)} -> (Data: {Identifier.ColorizeObject()}, Success: {succeeded.ColorizeObject()}, Expression: {condition})";
|
||||
}
|
||||
|
||||
@@ -66,13 +66,12 @@ namespace Barotrauma
|
||||
|
||||
public CheckItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
itemIdentifierSplit = ItemIdentifiers.Split(',').ToIdentifiers();
|
||||
itemTags = ItemTags.Split(",").ToIdentifiers();
|
||||
itemIdentifierSplit = ItemIdentifiers.ToIdentifiers().ToArray();
|
||||
itemTags = ItemTags.ToIdentifiers().ToArray();
|
||||
var conditionalList = new List<PropertyConditional>();
|
||||
foreach (ContentXElement subElement in element.GetChildElements("conditional"))
|
||||
{
|
||||
conditionalList.AddRange(PropertyConditional.FromXElement(subElement));
|
||||
break;
|
||||
}
|
||||
conditionals = conditionalList;
|
||||
|
||||
|
||||
@@ -113,13 +113,15 @@ namespace Barotrauma
|
||||
Text = elem.GetAttributeString("tag", string.Empty);
|
||||
textElement = elem;
|
||||
}
|
||||
}
|
||||
if (element.GetChildElement("Replace") != null)
|
||||
{
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in {nameof(EventObjectiveAction)} in the event \"{parentEvent.Prefab.Identifier}\"" +
|
||||
$" - unrecognized child element \"Replace\".",
|
||||
contentPackage: element.ContentPackage);
|
||||
else
|
||||
{
|
||||
string thisName = nameof(ConversationAction);
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in {thisName} in the event \"{parentEvent.Prefab.Identifier}\"" +
|
||||
$" - unrecognized child element \"{elem.Name}\". If it's an action intended to execute after the {thisName}, " +
|
||||
$"it should be after the {thisName}, not inside it.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +247,17 @@ namespace Barotrauma
|
||||
|
||||
public int[] GetEndingOptions()
|
||||
{
|
||||
List<int> endings = Options.Where(group => !group.Actions.Any() || group.EndConversation).Select(group => Options.IndexOf(group)).ToList();
|
||||
List<int> endings = Options
|
||||
.Where(group =>
|
||||
group.EndConversation ||
|
||||
//no actions = safe to assume this must end the conversation
|
||||
!group.Actions.Any() ||
|
||||
//no follow-up conversation and a goto makes the event jump somewhere else
|
||||
//we cannot easily determine whether that goto will lead to a follow-up conversation,
|
||||
//so it's safest to close this conversation to prevent it from getting stuck (the potential follow-up will open a new one)
|
||||
(group.Actions.None(a => a is ConversationAction) && group.Actions.Any(a => a is GoTo { EndConversation: true })))
|
||||
.Select(group => Options.IndexOf(group))
|
||||
.ToList();
|
||||
if (!ContinueConversation) { endings.Add(-1); }
|
||||
return endings.ToArray();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace Barotrauma
|
||||
{
|
||||
public string Text;
|
||||
public List<EventAction> Actions;
|
||||
/// <summary>
|
||||
/// Should this option end the conversation (closing the conversation prompt?). By default, options that don't have any actions inside them, or that only have a GoTo action, end the conversation.
|
||||
/// But if there are other actions inside the option, the game assumes there may be some kind of a follow-up coming to the conversation, and by default leaves it open.
|
||||
/// </summary>
|
||||
public bool EndConversation;
|
||||
|
||||
private int currentSubAction = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Barotrauma
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes the event jump to a <see cref="Label"/> somewhere else in the event.
|
||||
@@ -11,6 +11,9 @@ namespace Barotrauma
|
||||
[Serialize(-1, IsPropertySaveable.Yes, description: "How many times can this GoTo action be repeated? Can be used to make some parts of an event repeat a limited number of times. If negative or zero, there's no limit.")]
|
||||
public int MaxTimes { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "By default, jumping to another part in the event closes the active conversation prompt. Use this if if you want to keep it open instead.")]
|
||||
public bool EndConversation { get; set; }
|
||||
|
||||
private int counter;
|
||||
|
||||
public GoTo(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -213,7 +213,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!locationTypes.Contains(location.Type.Identifier) && !(location.HasOutpost() && locationTypes.Contains("AnyOutpost".ToIdentifier())))
|
||||
if (!locationTypes.Contains(location.Type.Identifier) && !(location.HasOutpost() && locationTypes.Contains(Tags.AnyOutpost)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Barotrauma
|
||||
namespace Barotrauma
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -31,6 +31,11 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MissionIdentifier has not been configured.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
if (Operation == OperationType.Add && State == 0)
|
||||
{
|
||||
DebugConsole.AddWarning($"Potential error in event \"{parentEvent.Prefab.Identifier}\": {nameof(MissionStateAction)} is set to add 0 to the mission state, which will do nothing.",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsFinished(ref string goTo)
|
||||
@@ -55,7 +60,7 @@ namespace Barotrauma
|
||||
mission.State = State;
|
||||
break;
|
||||
case OperationType.Add:
|
||||
mission.State += 1;
|
||||
mission.State += State;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+40
-23
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -42,13 +42,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
|
||||
bool isPlayerTeam = TeamID == CharacterTeamType.Team1 || TeamID == CharacterTeamType.Team2;
|
||||
bool isPlayerTeam = TeamID is CharacterTeamType.Team1 or CharacterTeamType.Team2;
|
||||
|
||||
affectedNpcs = ParentEvent.GetTargets(NPCTag).Where(c => c is Character).Select(c => c as Character).ToList();
|
||||
foreach (var npc in affectedNpcs)
|
||||
affectedNpcs = ParentEvent.GetTargets(NPCTag).OfType<Character>().ToList();
|
||||
foreach (Character npc in affectedNpcs)
|
||||
{
|
||||
// characters will still remain on friendlyNPC team for rest of the tick
|
||||
npc.SetOriginalTeam(TeamID);
|
||||
npc.SetOriginalTeamAndChangeTeam(TeamID);
|
||||
foreach (Item item in npc.Inventory.AllItems)
|
||||
{
|
||||
var idCard = item.GetComponent<Items.Components.IdCard>();
|
||||
@@ -61,26 +61,43 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (AddToCrew && isPlayerTeam)
|
||||
if (GameMain.GameSession.CrewManager is CrewManager crewManager)
|
||||
{
|
||||
npc.Info.StartItemsGiven = true;
|
||||
GameMain.GameSession.CrewManager.AddCharacter(npc);
|
||||
ChangeItemTeam(Submarine.MainSub, true);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
if (AddToCrew && isPlayerTeam)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.AddToCrewEventData(TeamID, npc.Inventory.AllItems));
|
||||
}
|
||||
}
|
||||
else if (RemoveFromCrew && (npc.TeamID == CharacterTeamType.Team1 || npc.TeamID == CharacterTeamType.Team2))
|
||||
{
|
||||
npc.Info.StartItemsGiven = true;
|
||||
GameMain.GameSession.CrewManager.RemoveCharacter(npc, removeInfo: true);
|
||||
var sub = Submarine.Loaded.FirstOrDefault(s => s.TeamID == TeamID);
|
||||
ChangeItemTeam(sub, false);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.RemoveFromCrewEventData(TeamID, npc.Inventory.AllItems));
|
||||
if (npc.Info is CharacterInfo info)
|
||||
{
|
||||
info.StartItemsGiven = true;
|
||||
crewManager.AddCharacter(npc);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Attempted to change the team of a character ({npc.Name}) that doesn't have Character Info. Can't add to the crew.");
|
||||
}
|
||||
ChangeItemTeam(Submarine.MainSub ?? Submarine.Loaded.FirstOrDefault(s => s.TeamID == TeamID), allowStealing: true);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.AddToCrewEventData(TeamID, npc.Inventory.AllItems));
|
||||
}
|
||||
}
|
||||
else if (RemoveFromCrew && npc.TeamID is CharacterTeamType.Team1 or CharacterTeamType.Team2)
|
||||
{
|
||||
if (npc.Info is CharacterInfo info)
|
||||
{
|
||||
info.StartItemsGiven = true;
|
||||
crewManager.RemoveCharacter(npc, removeInfo: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Attempted to change the team of a character ({npc.Name}) that doesn't have Character Info. Can't remove from the crew.");
|
||||
}
|
||||
Submarine sub = Submarine.Loaded.FirstOrDefault(s => s.TeamID == TeamID);
|
||||
ChangeItemTeam(sub, false);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new Character.RemoveFromCrewEventData(TeamID, npc.Inventory.AllItems));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeItemTeam(Submarine sub, bool allowStealing)
|
||||
@@ -98,7 +115,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
WayPoint subWaypoint =
|
||||
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ??
|
||||
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info?.Job?.Prefab) ??
|
||||
WayPoint.WayPointList.Find(wp => wp.Submarine == sub && wp.SpawnType == SpawnType.Human);
|
||||
if (subWaypoint != null)
|
||||
{
|
||||
|
||||
+2
-2
@@ -86,7 +86,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var objective in humanAiController.ObjectiveManager.Objectives)
|
||||
{
|
||||
if (objective is AIObjectiveOperateItem operateItemObjective && operateItemObjective.OperateTarget == target)
|
||||
if (objective is AIObjectiveOperateItem operateItemObjective && operateItemObjective.Component.Item == target)
|
||||
{
|
||||
objective.Abandon = true;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma
|
||||
if (npc.Removed || npc.AIController is not HumanAIController humanAiController) { continue; }
|
||||
foreach (var operateItemObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveOperateItem>())
|
||||
{
|
||||
if (operateItemObjective.OperateTarget == target)
|
||||
if (operateItemObjective.Component.Item == target)
|
||||
{
|
||||
operateItemObjective.Abandon = true;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Barotrauma
|
||||
{
|
||||
ItemIdentifiers = element.GetAttributeString("itemidentifier", element.GetAttributeString("identifier", string.Empty));
|
||||
}
|
||||
itemIdentifierSplit = ItemIdentifiers.Split(',').ToIdentifiers().ToImmutableHashSet();
|
||||
itemIdentifierSplit = ItemIdentifiers.ToIdentifiers().ToImmutableHashSet();
|
||||
}
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Barotrauma
|
||||
MainSub,
|
||||
Outpost,
|
||||
MainPath,
|
||||
Cave,
|
||||
AbyssCave,
|
||||
Ruin,
|
||||
Wreck,
|
||||
BeaconStation,
|
||||
@@ -38,6 +40,9 @@ namespace Barotrauma
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Identifier of the item to spawn.")]
|
||||
public Identifier ItemIdentifier { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Tag of the item to spawn.")]
|
||||
public Identifier ItemTag { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, 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 Identifier TargetTag { get; set; }
|
||||
|
||||
@@ -67,6 +72,9 @@ namespace Barotrauma
|
||||
[Serialize(1, IsPropertySaveable.Yes, description: "Number of entities to spawn.")]
|
||||
public int Amount { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Should the item be spawned even if the target inventory is full (just spawning it at the position of the target)? Only valid if spawning an item in an inventory.")]
|
||||
public bool SpawnIfInventoryFull { get; set; }
|
||||
|
||||
[Serialize(100.0f, IsPropertySaveable.Yes, description: "Random offset to add to the spawn position.")]
|
||||
public float Offset { get; set; }
|
||||
|
||||
@@ -94,6 +102,9 @@ namespace Barotrauma
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "If disabled, the action will choose a spawn position away from players' views if one is available.")]
|
||||
public bool AllowInPlayerView { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the event continue even if the entity failed to spawn for whatever reason?")]
|
||||
public bool ContinueIfFailedToSpawn { get; set; }
|
||||
|
||||
private bool spawned;
|
||||
private Entity spawnedEntity;
|
||||
|
||||
@@ -115,9 +126,9 @@ namespace Barotrauma
|
||||
|
||||
public override bool IsFinished(ref string goTo)
|
||||
{
|
||||
if (spawnedEntity != null)
|
||||
if (spawnedEntity != null || ContinueIfFailedToSpawn)
|
||||
{
|
||||
return true;
|
||||
return spawned;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -176,7 +187,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (newCharacter == null) { return; }
|
||||
newCharacter.HumanPrefab = humanPrefab;
|
||||
newCharacter.TeamID = TeamID;
|
||||
//don't set the TeamID directly: we want to leave the character's original team untouched,
|
||||
//so they can behave offensively (and otherwise act "normally") if we spawn them in a hostile team inside a sub/outpost that doesn't belong to that team
|
||||
|
||||
//process the team change immediately in case the character is killed or made unconscious by the event (in which case the team change would not be processed)
|
||||
newCharacter.SetOriginalTeamAndChangeTeam(TeamID, processImmediately: true);
|
||||
newCharacter.EnableDespawn = false;
|
||||
humanPrefab.GiveItems(newCharacter, newCharacter.Submarine, spawnPos as WayPoint);
|
||||
if (LootingIsStealing)
|
||||
@@ -233,74 +248,82 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!ItemIdentifier.IsEmpty)
|
||||
else if (!ItemIdentifier.IsEmpty || !ItemTag.IsEmpty)
|
||||
{
|
||||
if (MapEntityPrefab.FindByIdentifier(ItemIdentifier) is not ItemPrefab itemPrefab)
|
||||
ItemPrefab itemPrefab = null;
|
||||
if (!ItemIdentifier.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SpawnAction (item prefab \"" + ItemIdentifier + "\" not found)",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory spawnInventory = null;
|
||||
if (!TargetInventory.IsEmpty)
|
||||
itemPrefab = MapEntityPrefab.FindByIdentifier(ItemIdentifier) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
DebugConsole.ThrowError($"Error in SpawnAction (item prefab \"{ItemIdentifier}\" not found)",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
else if (!ItemTag.IsEmpty)
|
||||
{
|
||||
itemPrefab = ItemPrefab.Prefabs.Where(ip => ip.Tags.Contains(ItemTag)).GetRandom(Rand.RandSync.Unsynced);
|
||||
}
|
||||
|
||||
if (spawnInventory == null)
|
||||
Inventory spawnInventory = null;
|
||||
if (!TargetInventory.IsEmpty)
|
||||
{
|
||||
var targets = ParentEvent.GetTargets(TargetInventory);
|
||||
if (targets.Any())
|
||||
{
|
||||
var target = targets.First(t => t is Item || t is Character);
|
||||
if (target is Character character)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not spawn \"{ItemIdentifier}\" in target inventory \"{TargetInventory}\" - matching target not found.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
spawnInventory = character.Inventory;
|
||||
}
|
||||
else if (target is Item item)
|
||||
{
|
||||
spawnInventory = item.OwnInventory;
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnInventory == null)
|
||||
{
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
for (int i = 0; i < Amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, OffsetSpawnPos(spawnPos.WorldPosition, Rand.Range(0.0f, Offset)), onSpawned: onSpawned);
|
||||
}
|
||||
}
|
||||
DebugConsole.ThrowError($"Could not spawn \"{ItemIdentifier}\" in target inventory \"{TargetInventory}\" - matching target not found.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
if (spawnInventory == null)
|
||||
{
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
for (int i = 0; i < Amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, spawnInventory, onSpawned: onSpawned);
|
||||
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, OffsetSpawnPos(spawnPos.WorldPosition, Rand.Range(0.0f, Offset)), onSpawned: onSpawned);
|
||||
}
|
||||
}
|
||||
void onSpawned(Item newItem)
|
||||
{
|
||||
if (newItem != null)
|
||||
{
|
||||
if (!TargetTag.IsEmpty)
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newItem);
|
||||
}
|
||||
if (IgnoreByAI)
|
||||
{
|
||||
newItem.AddTag("ignorebyai");
|
||||
}
|
||||
}
|
||||
spawnedEntity = newItem;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, spawnInventory, spawnIfInventoryFull: SpawnIfInventoryFull, onSpawned: onSpawned);
|
||||
|
||||
}
|
||||
}
|
||||
void onSpawned(Item newItem)
|
||||
{
|
||||
if (newItem != null)
|
||||
{
|
||||
if (!TargetTag.IsEmpty)
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newItem);
|
||||
}
|
||||
if (IgnoreByAI)
|
||||
{
|
||||
newItem.AddTag("ignorebyai");
|
||||
}
|
||||
}
|
||||
spawnedEntity = newItem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
spawned = true;
|
||||
@@ -353,8 +376,7 @@ namespace Barotrauma
|
||||
{
|
||||
SpawnLocationType.Any => true,
|
||||
SpawnLocationType.MainSub => submarine == Submarine.MainSub,
|
||||
SpawnLocationType.NearMainSub => submarine == null,
|
||||
SpawnLocationType.MainPath => submarine == null,
|
||||
SpawnLocationType.NearMainSub or SpawnLocationType.MainPath or SpawnLocationType.Cave or SpawnLocationType.AbyssCave => submarine == null,
|
||||
SpawnLocationType.Outpost => submarine is { Info.IsOutpost: true },
|
||||
SpawnLocationType.Wreck => submarine is { Info.IsWreck: true },
|
||||
SpawnLocationType.Ruin => submarine is { Info.IsRuin: true },
|
||||
@@ -443,10 +465,25 @@ namespace Barotrauma
|
||||
return potentialSpawnPoints.GetRandomUnsynced();
|
||||
}
|
||||
|
||||
if (spawnLocation == SpawnLocationType.MainPath || spawnLocation == SpawnLocationType.NearMainSub)
|
||||
switch (spawnLocation)
|
||||
{
|
||||
validSpawnPoints = validSpawnPoints.Where(p =>
|
||||
Submarine.Loaded.None(s => ToolBox.GetWorldBounds(s.Borders.Center, s.Borders.Size).ContainsWorld(p.WorldPosition)));
|
||||
case SpawnLocationType.MainPath:
|
||||
case SpawnLocationType.NearMainSub:
|
||||
validSpawnPoints = validSpawnPoints.Where(p =>
|
||||
Submarine.Loaded.None(s => ToolBox.GetWorldBounds(s.Borders.Center, s.Borders.Size).ContainsWorld(p.WorldPosition)));
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
validSpawnPoints = validSpawnPoints.Where(p =>
|
||||
p.WorldPosition.Y > Level.Loaded.AbyssStart &&
|
||||
p.Cave == null && p.Ruin == null);
|
||||
}
|
||||
break;
|
||||
case SpawnLocationType.Cave:
|
||||
validSpawnPoints = validSpawnPoints.Where(p => p.WorldPosition.Y > Level.Loaded.AbyssStart && p.Cave != null);
|
||||
break;
|
||||
case SpawnLocationType.AbyssCave:
|
||||
validSpawnPoints = validSpawnPoints.Where(p => p.WorldPosition.Y < Level.Loaded.AbyssStart && p.Cave != null);
|
||||
break;
|
||||
}
|
||||
|
||||
//avoid using waypoints if there's any actual spawnpoints available
|
||||
|
||||
@@ -11,9 +11,10 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
class TagAction : EventAction
|
||||
{
|
||||
public enum SubType { Any = 0, Player = 1, Outpost = 2, Wreck = 4, BeaconStation = 8 }
|
||||
public enum SubType { Any = 0, Player = 1, Outpost = 2, Wreck = 4, BeaconStation = 8, Enemy = 16, Ruin = 32 }
|
||||
public enum CharacterTeam { Any = 0, None = 1, Team1 = 2, Team2 = 4, FriendlyNPC = 8 }
|
||||
|
||||
[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].")]
|
||||
[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], speciesname:[id].")]
|
||||
public string Criteria { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "The tag to apply to the target.")]
|
||||
@@ -22,6 +23,9 @@ namespace Barotrauma
|
||||
[Serialize(SubType.Any, IsPropertySaveable.Yes, description: "The type of submarine the target needs to be in.")]
|
||||
public SubType SubmarineType { get; set; }
|
||||
|
||||
[Serialize(CharacterTeam.Any, IsPropertySaveable.Yes, description: "The team the target needs to be on.")]
|
||||
public CharacterTeam Team { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, "If set, the target must be in an outpost module that has this tag.")]
|
||||
public Identifier RequiredModuleTag { get; set; }
|
||||
|
||||
@@ -34,6 +38,9 @@ namespace Barotrauma
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If there are multiple matching targets, should all of them be tagged or one chosen randomly?")]
|
||||
public bool ChooseRandom { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "If choosing a random target, targets with this tag can optionally be excluded.")]
|
||||
public Identifier ChooseRandomExcludingTag { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the event continue if the TagAction can't find any valid targets?")]
|
||||
public bool ContinueIfNoTargetsFound { get; set; }
|
||||
|
||||
@@ -78,6 +85,7 @@ namespace Barotrauma
|
||||
("hullname", TagHullsByName),
|
||||
("submarine", TagSubmarinesByType),
|
||||
("eventtag", TagByEventTag),
|
||||
("speciesname", TagBySpeciesName)
|
||||
}.Select(t => (t.k.ToIdentifier(), t.v)).ToImmutableDictionary();
|
||||
}
|
||||
|
||||
@@ -87,9 +95,16 @@ namespace Barotrauma
|
||||
}
|
||||
public override void Reset()
|
||||
{
|
||||
taggingDone = false;
|
||||
cantFindTargets = false;
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
private void TagBySpeciesName(Identifier speciesName)
|
||||
{
|
||||
AddTarget(Tag, Character.CharacterList.Where(c => c.SpeciesName == speciesName && CharacterTeamMatches(c)));
|
||||
}
|
||||
|
||||
private void TagByEventTag(Identifier eventTag)
|
||||
{
|
||||
AddTarget(Tag, ParentEvent.GetTargets(eventTag).Where(t => MatchesRequirements(t)));
|
||||
@@ -100,7 +115,7 @@ namespace Barotrauma
|
||||
AddTargetPredicate(
|
||||
Tag,
|
||||
ScriptedEvent.TargetPredicate.EntityType.Character,
|
||||
e => e is Character c && c.IsPlayer && (!c.IsIncapacitated || !IgnoreIncapacitatedCharacters));
|
||||
e => e is Character c && c.IsPlayer && (!c.IsIncapacitated || !IgnoreIncapacitatedCharacters) && CharacterTeamMatches(c));
|
||||
}
|
||||
|
||||
private void TagTraitors()
|
||||
@@ -151,7 +166,7 @@ namespace Barotrauma
|
||||
|
||||
private void TagHumansByIdentifier(Identifier identifier)
|
||||
{
|
||||
AddTarget(Tag, Character.CharacterList.Where(c => c.HumanPrefab?.Identifier == identifier));
|
||||
AddTarget(Tag, Character.CharacterList.Where(c => c.HumanPrefab?.Identifier == identifier && CharacterTeamMatches(c)));
|
||||
}
|
||||
|
||||
private void TagHumansByTag(Identifier tag)
|
||||
@@ -161,7 +176,7 @@ namespace Barotrauma
|
||||
|
||||
private void TagHumansByJobIdentifier(Identifier jobIdentifier)
|
||||
{
|
||||
AddTarget(Tag, Character.CharacterList.Where(c => c.HasJob(jobIdentifier)));
|
||||
AddTarget(Tag, Character.CharacterList.Where(c => c.HasJob(jobIdentifier) && CharacterTeamMatches(c)));
|
||||
}
|
||||
|
||||
private void TagStructuresByIdentifier(Identifier identifier)
|
||||
@@ -233,7 +248,7 @@ namespace Barotrauma
|
||||
|
||||
private bool MatchesRequirements(Entity e)
|
||||
{
|
||||
return ModuleTagMatches(e) && SubmarineTypeMatches(e.Submarine);
|
||||
return ModuleTagMatches(e) && SubmarineTypeMatches(e as Submarine ?? e.Submarine);
|
||||
}
|
||||
|
||||
private bool ModuleTagMatches(Entity e)
|
||||
@@ -267,6 +282,23 @@ namespace Barotrauma
|
||||
return hull != null && hull.OutpostModuleTags.Contains(RequiredModuleTag);
|
||||
}
|
||||
|
||||
private bool CharacterTeamMatches(Character character)
|
||||
{
|
||||
if (Team == CharacterTeam.Any) { return true; }
|
||||
switch (Team)
|
||||
{
|
||||
case CharacterTeam.None:
|
||||
return character.TeamID == CharacterTeamType.None;
|
||||
case CharacterTeam.Team1:
|
||||
return character.TeamID == CharacterTeamType.Team1;
|
||||
case CharacterTeam.Team2:
|
||||
return character.TeamID == CharacterTeamType.Team2;
|
||||
case CharacterTeam.FriendlyNPC:
|
||||
return character.TeamID == CharacterTeamType.FriendlyNPC;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool SubmarineTypeMatches(Submarine sub)
|
||||
{
|
||||
@@ -280,7 +312,7 @@ namespace Barotrauma
|
||||
switch (sub.Info.Type)
|
||||
{
|
||||
case Barotrauma.SubmarineType.Player:
|
||||
return submarineType.HasFlag(SubType.Player) && sub != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle;
|
||||
return submarineType.HasFlag(SubType.Player) && !sub.IsRespawnShuttle;
|
||||
case Barotrauma.SubmarineType.Outpost:
|
||||
case Barotrauma.SubmarineType.OutpostModule:
|
||||
return submarineType.HasFlag(SubType.Outpost);
|
||||
@@ -288,6 +320,10 @@ namespace Barotrauma
|
||||
return submarineType.HasFlag(SubType.Wreck);
|
||||
case Barotrauma.SubmarineType.BeaconStation:
|
||||
return submarineType.HasFlag(SubType.BeaconStation);
|
||||
case Barotrauma.SubmarineType.EnemySubmarine:
|
||||
return submarineType.HasFlag(SubType.Enemy);
|
||||
case Barotrauma.SubmarineType.Ruin:
|
||||
return submarineType.HasFlag(SubType.Ruin);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -357,6 +393,11 @@ namespace Barotrauma
|
||||
|
||||
private void TagRandom(Identifier tag, IEnumerable<Entity> entities)
|
||||
{
|
||||
if (!ChooseRandomExcludingTag.IsEmpty)
|
||||
{
|
||||
var excludedTargets = ParentEvent.GetTargets(ChooseRandomExcludingTag);
|
||||
entities = entities.Except(excludedTargets);
|
||||
}
|
||||
if (entities.None())
|
||||
{
|
||||
cantFindTargets = true;
|
||||
@@ -414,7 +455,7 @@ namespace Barotrauma
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()}, Sub: {SubmarineType.ColorizeObject()})";
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()}, Sub: {SubmarineType.ColorizeObject()}, Team: {Team.ColorizeObject()})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -56,7 +56,16 @@ namespace Barotrauma
|
||||
|
||||
private float distance;
|
||||
|
||||
public TriggerAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
|
||||
public TriggerAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
if (element.GetAttribute(nameof(TagAction.IgnoreIncapacitatedCharacters)) != null)
|
||||
{
|
||||
DebugConsole.AddWarning(
|
||||
$"Potential error in {nameof(TriggerAction)}, event \"{parentEvent.Prefab.Identifier}\": "+
|
||||
$"{nameof(TagAction.IgnoreIncapacitatedCharacters)} is a property of {nameof(TagAction)}, did you mean {nameof(DisableIfTargetIncapacitated)}?",
|
||||
contentPackage: element.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
private bool isFinished = false;
|
||||
public override bool IsFinished(ref string goTo)
|
||||
@@ -157,13 +166,13 @@ namespace Barotrauma
|
||||
Item item = null;
|
||||
if (e1 is Character char1)
|
||||
{
|
||||
if (char1.IsBot)
|
||||
{
|
||||
npc ??= char1;
|
||||
if (char1.IsPlayer)
|
||||
{
|
||||
player = char1;
|
||||
}
|
||||
else
|
||||
{
|
||||
player = char1;
|
||||
else
|
||||
{
|
||||
npc ??= char1;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -172,13 +181,13 @@ namespace Barotrauma
|
||||
}
|
||||
if (e2 is Character char2)
|
||||
{
|
||||
if (char2.IsBot)
|
||||
{
|
||||
npc ??= char2;
|
||||
}
|
||||
else
|
||||
if (char2.IsPlayer)
|
||||
{
|
||||
player = char2;
|
||||
player = char2;
|
||||
}
|
||||
else
|
||||
{
|
||||
npc ??= char2;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -190,6 +199,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (npc != null)
|
||||
{
|
||||
if (!npcsOrItems.Any(n => n.TryGet(out Character npc2) && npc2 == npc))
|
||||
{
|
||||
npcsOrItems.Add(npc);
|
||||
}
|
||||
if (npc.CampaignInteractionType == CampaignMode.InteractionType.Talk)
|
||||
{
|
||||
//if the NPC has a conversation available, don't assign the trigger until the conversation is done
|
||||
@@ -197,10 +210,6 @@ namespace Barotrauma
|
||||
}
|
||||
else if (npc.CampaignInteractionType != CampaignMode.InteractionType.Examine)
|
||||
{
|
||||
if (!npcsOrItems.Any(n => n.TryGet(out Character npc2) && npc2 == npc))
|
||||
{
|
||||
npcsOrItems.Add(npc);
|
||||
}
|
||||
npc.CampaignInteractionType = CampaignMode.InteractionType.Examine;
|
||||
npc.RequireConsciousnessForCustomInteract = DisableIfTargetIncapacitated;
|
||||
#if CLIENT
|
||||
@@ -339,7 +348,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (c.AIController is EnemyAIController enemyAI && (enemyAI.State == AIState.Aggressive || enemyAI.State == AIState.Attack))
|
||||
else if (c.AIController is EnemyAIController { State: AIState.Aggressive or AIState.Attack } enemyAI)
|
||||
{
|
||||
if (enemyAI.SelectedAiTarget?.Entity == character || c.CurrentHull == character.CurrentHull)
|
||||
{
|
||||
@@ -401,10 +410,18 @@ namespace Barotrauma
|
||||
{
|
||||
if (TargetModuleType.IsEmpty)
|
||||
{
|
||||
string targetStr = "none";
|
||||
if (npcsOrItems.Any())
|
||||
{
|
||||
targetStr = string.Join(", ",
|
||||
npcsOrItems.Select(npcOrItem =>
|
||||
npcOrItem.TryGet(out Character character) ? character.Name : (npcOrItem.TryGet(out Item item) ? item.Name : "none")));
|
||||
}
|
||||
|
||||
return
|
||||
$"{ToolBox.GetDebugSymbol(isFinished, isRunning)} {nameof(TriggerAction)} -> (" +
|
||||
(WaitForInteraction ?
|
||||
$"Selected non-player target: {(npcsOrItems?.ToString() ?? "<null>").ColorizeObject()}, " :
|
||||
$"Selected non-player target: {targetStr.ColorizeObject()}, " :
|
||||
$"Distance: {((int)distance).ColorizeObject()}, ") +
|
||||
$"Radius: {Radius.ColorizeObject()}, " +
|
||||
$"TargetTags: {Target1Tag.ColorizeObject()}, " +
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggers another scripted event.
|
||||
/// Triggers another event (can also trigger things other than scripted events, for example monster events).
|
||||
/// </summary>
|
||||
class TriggerEventAction : EventAction
|
||||
{
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Identifier of the event to trigger.")]
|
||||
public Identifier Identifier { get; set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "Tag of the event to trigger.")]
|
||||
public Identifier EventTag { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If set to true, the event will trigger at the beginning of the next round. Useful for e.g. triggering some scripted event in the outpost after you finish a mission.")]
|
||||
public bool NextRound { get; set; }
|
||||
|
||||
@@ -36,13 +39,8 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
var eventPrefab = EventSet.GetEventPrefab(Identifier);
|
||||
if (eventPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in TriggerEventAction - could not find an event with the identifier {Identifier}.",
|
||||
contentPackage: ParentEvent.Prefab.ContentPackage);
|
||||
}
|
||||
else
|
||||
EventPrefab eventPrefab = EventPrefab.FindEventPrefab(Identifier, EventTag, ParentEvent.Prefab.ContentPackage);
|
||||
if (eventPrefab != null)
|
||||
{
|
||||
var ev = eventPrefab.CreateInstance(GameMain.GameSession.EventManager.RandomSeed);
|
||||
if (ev != null)
|
||||
|
||||
Reference in New Issue
Block a user