v0.14.6.0
This commit is contained in:
@@ -23,7 +23,17 @@ namespace Barotrauma
|
||||
|
||||
protected PropertyConditional.OperatorType Operator { get; set; }
|
||||
|
||||
public CheckDataAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
|
||||
public CheckDataAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Condition))
|
||||
{
|
||||
Condition = element.GetAttributeString("value", string.Empty);
|
||||
if (string.IsNullOrEmpty(Condition))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in scripted event \"{parentEvent.Prefab.Identifier}\". CheckDataAction with no condition set ({element}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (speaker == null) { return; }
|
||||
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||
speaker.ActiveConversation = this;
|
||||
speaker.ActiveConversation = null;
|
||||
speaker.SetCustomInteract(null, null);
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(speaker, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
|
||||
@@ -213,7 +213,14 @@ namespace Barotrauma
|
||||
if (dialogOpened)
|
||||
{
|
||||
#if CLIENT
|
||||
Character.DisableControls = true;
|
||||
if (GUIMessageBox.MessageBoxes.Any(mb => mb.UserData as string == "ConversationAction"))
|
||||
{
|
||||
Character.DisableControls = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
#endif
|
||||
if (ShouldInterrupt())
|
||||
{
|
||||
@@ -240,7 +247,7 @@ namespace Barotrauma
|
||||
{
|
||||
TryStartConversation(speaker);
|
||||
}
|
||||
else
|
||||
else if (speaker.ActiveConversation != this)
|
||||
{
|
||||
speaker.CampaignInteractionType = CampaignMode.InteractionType.Talk;
|
||||
speaker.ActiveConversation = this;
|
||||
@@ -303,9 +310,15 @@ namespace Barotrauma
|
||||
|
||||
private bool IsValidTarget(Entity e)
|
||||
{
|
||||
return
|
||||
e is Character character && !character.Removed && !character.IsDead && !character.IsIncapacitated &&
|
||||
bool isValid = e is Character character && !character.Removed && !character.IsDead && !character.IsIncapacitated &&
|
||||
(e == Character.Controlled || character.IsRemotePlayer);
|
||||
#if SERVER
|
||||
UpdateIgnoredClients();
|
||||
isValid &= !ignoredClients.Keys.Any(c => c.Character == e);
|
||||
#elif CLIENT
|
||||
isValid &= (e != Character.Controlled || !GUI.InputBlockingMenuOpen);
|
||||
#endif
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void TryStartConversation(Character speaker, Character targetCharacter = null)
|
||||
@@ -348,10 +361,18 @@ namespace Barotrauma
|
||||
{
|
||||
ParentEvent.AddTarget(InvokerTag, targetCharacter);
|
||||
}
|
||||
|
||||
|
||||
ShowDialog(speaker, targetCharacter);
|
||||
|
||||
dialogOpened = true;
|
||||
if (speaker != null)
|
||||
{
|
||||
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||
speaker.SetCustomInteract(null, null);
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(speaker, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
partial void ShowDialog(Character speaker, Character targetCharacter);
|
||||
|
||||
@@ -14,6 +14,18 @@ namespace Barotrauma
|
||||
[Serialize("", true)]
|
||||
public string MissionTag { get; set; }
|
||||
|
||||
[Serialize("", true, description: "The type of the location the mission will be unlocked in (if empty, any location can be selected).")]
|
||||
public string LocationType { get; set; }
|
||||
|
||||
[Serialize(0, true, description: "Minimum distance to the location the mission is unlocked in (1 = one path between locations).")]
|
||||
public int MinLocationDistance { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "If true, the mission has to be unlocked in a location further on the campaign map.")]
|
||||
public bool UnlockFurtherOnMap { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "If true, a suitable location is forced on the map if one isn't found.")]
|
||||
public bool CreateLocationIfNotFound { get; set; }
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
public MissionAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
|
||||
@@ -44,33 +56,82 @@ namespace Barotrauma
|
||||
if (GameMain.GameSession.GameMode is CampaignMode campaign)
|
||||
{
|
||||
MissionPrefab prefab = null;
|
||||
if (!string.IsNullOrEmpty(MissionIdentifier))
|
||||
var unlockLocation = FindUnlockLocation();
|
||||
if (unlockLocation == null && CreateLocationIfNotFound)
|
||||
{
|
||||
prefab = campaign.Map.CurrentLocation.UnlockMissionByIdentifier(MissionIdentifier);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(MissionTag))
|
||||
{
|
||||
prefab = campaign.Map.CurrentLocation.UnlockMissionByTag(MissionTag);
|
||||
}
|
||||
if (campaign is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.LastUpdateID++;
|
||||
//find an empty location at least 3 steps away, further on the map
|
||||
var emptyLocation = FindUnlockLocationRecursive(campaign.Map.CurrentLocation, Math.Max(MinLocationDistance, 3), "none", true, new HashSet<Location>());
|
||||
if (emptyLocation != null)
|
||||
{
|
||||
emptyLocation.ChangeType(Barotrauma.LocationType.List.Find(lt => lt.Identifier.Equals(LocationType, StringComparison.OrdinalIgnoreCase)));
|
||||
unlockLocation = emptyLocation;
|
||||
}
|
||||
}
|
||||
|
||||
if (prefab != null)
|
||||
if (unlockLocation != 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))
|
||||
if (!string.IsNullOrEmpty(MissionIdentifier))
|
||||
{
|
||||
IconColor = prefab.IconColor
|
||||
};
|
||||
#else
|
||||
NotifyMissionUnlock(prefab);
|
||||
#endif
|
||||
prefab = unlockLocation.UnlockMissionByIdentifier(MissionIdentifier);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(MissionTag))
|
||||
{
|
||||
prefab = unlockLocation.UnlockMissionByTag(MissionTag);
|
||||
}
|
||||
if (campaign is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.LastUpdateID++;
|
||||
}
|
||||
if (prefab != null)
|
||||
{
|
||||
DebugConsole.NewMessage($"Unlocked mission \"{prefab.Name}\" in the location \"{unlockLocation.Name}\".");
|
||||
#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
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to find a suitable location to unlock a mission in (LocationType: {LocationType}, MinLocationDistance: {MinLocationDistance}, UnlockFurtherOnMap: {UnlockFurtherOnMap})");
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
private Location FindUnlockLocation()
|
||||
{
|
||||
var campaign = GameMain.GameSession.GameMode as CampaignMode;
|
||||
if (string.IsNullOrEmpty(LocationType) && MinLocationDistance <= 1)
|
||||
{
|
||||
return campaign.Map.CurrentLocation;
|
||||
}
|
||||
|
||||
return FindUnlockLocationRecursive(campaign.Map.CurrentLocation, 0, LocationType, UnlockFurtherOnMap, new HashSet<Location>());
|
||||
}
|
||||
|
||||
private Location FindUnlockLocationRecursive(Location currLocation, int currDistance, string locationType, bool unlockFurtherOnMap, HashSet<Location> checkedLocations)
|
||||
{
|
||||
var campaign = GameMain.GameSession.GameMode as CampaignMode;
|
||||
if (currLocation.Type.Identifier.Equals(locationType, StringComparison.OrdinalIgnoreCase) && currDistance >= MinLocationDistance &&
|
||||
(!unlockFurtherOnMap || currLocation.MapPosition.X > campaign.Map.CurrentLocation.MapPosition.X))
|
||||
{
|
||||
return currLocation;
|
||||
}
|
||||
checkedLocations.Add(currLocation);
|
||||
foreach (LocationConnection connection in currLocation.Connections)
|
||||
{
|
||||
var otherLocation = connection.OtherLocation(currLocation);
|
||||
if (checkedLocations.Contains(otherLocation)) { continue; }
|
||||
var unlockLocation = FindUnlockLocationRecursive(otherLocation, ++currDistance, locationType, unlockFurtherOnMap, checkedLocations);
|
||||
if (unlockLocation != null) { return unlockLocation; }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
@@ -84,8 +145,8 @@ namespace Barotrauma
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte) ServerPacketHeader.EVENTACTION);
|
||||
outmsg.Write((byte) EventManager.NetworkEventType.MISSION);
|
||||
outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.Write((byte)EventManager.NetworkEventType.MISSION);
|
||||
outmsg.Write(prefab.Identifier);
|
||||
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class NPCChangeTeamAction : EventAction
|
||||
{
|
||||
[Serialize("", true)]
|
||||
public string NPCTag { get; set; }
|
||||
|
||||
[Serialize(0, true)]
|
||||
public int TeamTag { get; set; }
|
||||
|
||||
[Serialize(false, true)]
|
||||
public bool AddToCrew { get; set; }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public NPCChangeTeamAction(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)
|
||||
{
|
||||
CharacterTeamType newTeam = (CharacterTeamType)TeamTag;
|
||||
// characters will still remain on friendlyNPC team for rest of the tick
|
||||
npc.SetOriginalTeam(newTeam);
|
||||
|
||||
if (AddToCrew && (newTeam == CharacterTeamType.Team1 || newTeam == CharacterTeamType.Team2))
|
||||
{
|
||||
npc.Info.StartItemsGiven = true;
|
||||
|
||||
GameMain.GameSession.CrewManager.AddCharacter(npc);
|
||||
foreach (Item item in npc.Inventory.AllItems)
|
||||
{
|
||||
item.AllowStealing = true;
|
||||
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>();
|
||||
if (wifiComponent != null)
|
||||
{
|
||||
wifiComponent.TeamID = newTeam;
|
||||
}
|
||||
}
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AddToCrew, newTeam, npc.Inventory.AllItems.Select(it => it.ID).ToArray() });
|
||||
#endif
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override bool IsFinished(ref string goTo)
|
||||
{
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(NPCChangeTeamAction)} -> (NPCTag: {NPCTag.ColorizeObject()})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,9 +48,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var goToObjective in humanAiController.ObjectiveManager.GetActiveObjectives<AIObjectiveGoTo>())
|
||||
foreach (var objective in humanAiController.ObjectiveManager.Objectives)
|
||||
{
|
||||
if (goToObjective.Target == target)
|
||||
if (objective is AIObjectiveGoTo goToObjective && goToObjective.Target == target)
|
||||
{
|
||||
goToObjective.Abandon = true;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
ItemIdentifier = element.GetAttributeString("itemidentifiers", "");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\" - RemoveItemAction without an item identifier.");
|
||||
ItemIdentifier = element.GetAttributeString("itemidentifiers", null) ?? element.GetAttributeString("identifier", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +43,7 @@ namespace Barotrauma
|
||||
bool hasValidTargets = false;
|
||||
foreach (Entity target in targets)
|
||||
{
|
||||
if (target is Character character && character.Inventory != null)
|
||||
if (target is Character character && character.Inventory != null || target is Item)
|
||||
{
|
||||
hasValidTargets = true;
|
||||
break;
|
||||
@@ -55,20 +51,31 @@ namespace Barotrauma
|
||||
}
|
||||
if (!hasValidTargets) { return; }
|
||||
|
||||
List<Item> usedItems = new List<Item>();
|
||||
HashSet<Item> removedItems = new HashSet<Item>();
|
||||
foreach (Entity target in targets)
|
||||
{
|
||||
Inventory inventory = (target as Character)?.Inventory;
|
||||
if (inventory == null) { continue; }
|
||||
while (usedItems.Count < Amount)
|
||||
if (inventory != null)
|
||||
{
|
||||
var item = inventory.FindItem(it =>
|
||||
it != null &&
|
||||
!usedItems.Contains(it) &&
|
||||
it.Prefab.Identifier.Equals(ItemIdentifier, StringComparison.InvariantCultureIgnoreCase), recursive: true);
|
||||
if (item == null) { break; }
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
usedItems.Add(item);
|
||||
while (removedItems.Count < Amount)
|
||||
{
|
||||
var item = inventory.FindItem(it =>
|
||||
it != null &&
|
||||
!removedItems.Contains(it) &&
|
||||
(string.IsNullOrEmpty(ItemIdentifier) || it.Prefab.Identifier.Equals(ItemIdentifier, StringComparison.InvariantCultureIgnoreCase)), recursive: true);
|
||||
if (item == null) { break; }
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
removedItems.Add(item);
|
||||
}
|
||||
}
|
||||
else if (target is Item item)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ItemIdentifier) || item.Prefab.Identifier.Equals(ItemIdentifier, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
removedItems.Add(item);
|
||||
if (removedItems.Count >= Amount) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace Barotrauma
|
||||
Outpost,
|
||||
MainPath,
|
||||
Ruin,
|
||||
Wreck
|
||||
Wreck,
|
||||
BeaconStation
|
||||
}
|
||||
|
||||
[Serialize("", true, description: "Species name of the character to spawn.")]
|
||||
@@ -102,31 +103,36 @@ namespace Barotrauma
|
||||
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 =>
|
||||
if (humanPrefab != null)
|
||||
{
|
||||
newCharacter.TeamID = CharacterTeamType.FriendlyNPC;
|
||||
newCharacter.EnableDespawn = false;
|
||||
humanPrefab.GiveItems(newCharacter, newCharacter.Submarine);
|
||||
if (LootingIsStealing)
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
Entity.Spawner.AddToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos?.WorldPosition ?? Vector2.Zero, 100.0f), humanPrefab.GetCharacterInfo(), onSpawn: newCharacter =>
|
||||
{
|
||||
foreach (Item item in newCharacter.Inventory.AllItems)
|
||||
if (newCharacter == null) { return; }
|
||||
newCharacter.Prefab = humanPrefab;
|
||||
newCharacter.TeamID = CharacterTeamType.FriendlyNPC;
|
||||
newCharacter.EnableDespawn = false;
|
||||
humanPrefab.GiveItems(newCharacter, newCharacter.Submarine);
|
||||
if (LootingIsStealing)
|
||||
{
|
||||
item.SpawnedInOutpost = true;
|
||||
item.AllowStealing = false;
|
||||
foreach (Item item in newCharacter.Inventory.AllItems)
|
||||
{
|
||||
item.SpawnedInOutpost = true;
|
||||
item.AllowStealing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
humanPrefab.InitializeCharacter(newCharacter, spawnPos);
|
||||
if (!string.IsNullOrEmpty(TargetTag) && newCharacter != null)
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newCharacter);
|
||||
}
|
||||
spawnedEntity = newCharacter;
|
||||
});
|
||||
humanPrefab.InitializeCharacter(newCharacter, spawnPos);
|
||||
if (!string.IsNullOrEmpty(TargetTag) && newCharacter != null)
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newCharacter);
|
||||
}
|
||||
spawnedEntity = newCharacter;
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(SpeciesName))
|
||||
{
|
||||
@@ -196,8 +202,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
spawned = true;
|
||||
|
||||
spawned = true;
|
||||
}
|
||||
|
||||
public static Vector2 OffsetSpawnPos(Vector2 pos, float offsetAmount)
|
||||
@@ -225,6 +230,7 @@ namespace Barotrauma
|
||||
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),
|
||||
SpawnLocationType.BeaconStation => Item.ItemList.FindAll(it => it.Submarine != null && it.Submarine.Info.IsBeacon),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
@@ -250,15 +256,15 @@ namespace Barotrauma
|
||||
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),
|
||||
SpawnLocationType.BeaconStation => WayPoint.WayPointList.FindAll(wp => wp.Submarine != null && wp.Submarine.Info.IsBeacon),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
potentialSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.ConnectedDoor == null && wp.Ladders == null && !wp.isObstructed);
|
||||
|
||||
var airlockSpawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false).ToList();
|
||||
if (moduleFlags != null && moduleFlags.Any())
|
||||
{
|
||||
List<WayPoint> spawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags?.Any(moduleFlags.Contains) ?? false).ToList();
|
||||
List<WayPoint> spawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags.Any(moduleFlags.Contains) ?? false).ToList();
|
||||
if (spawnPoints.Any())
|
||||
{
|
||||
potentialSpawnPoints = spawnPoints;
|
||||
@@ -267,8 +273,10 @@ namespace Barotrauma
|
||||
|
||||
if (spawnpointTags != null && spawnpointTags.Any())
|
||||
{
|
||||
var spawnPoints = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag)))
|
||||
.Where(wp => wp.ConnectedDoor == null && !wp.isObstructed);
|
||||
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();
|
||||
@@ -293,6 +301,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//don't spawn in an airlock module if there are other options
|
||||
var airlockSpawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags.Contains("airlock") ?? false);
|
||||
if (airlockSpawnPoints.Count() < validSpawnPoints.Count())
|
||||
{
|
||||
validSpawnPoints = validSpawnPoints.Except(airlockSpawnPoints);
|
||||
|
||||
@@ -51,7 +51,14 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var target in targets)
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, target, target as ISerializableEntity);
|
||||
if (target is Item targetItem)
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, target, targetItem.AllPropertyObjects);
|
||||
}
|
||||
else
|
||||
{
|
||||
effect.Apply(effect.type, deltaTime, target, target as ISerializableEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if SERVER
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Barotrauma
|
||||
{
|
||||
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 }
|
||||
|
||||
[Serialize("", true)]
|
||||
public string Criteria { get; set; }
|
||||
@@ -67,6 +67,16 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
private void TagHumansByIdentifier(string identifier)
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.Prefab?.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase) ?? false)
|
||||
{
|
||||
ParentEvent.AddTarget(Tag, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void TagStructuresByIdentifier(string identifier)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && SubmarineTypeMatches(s.Submarine) && s.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
|
||||
@@ -122,6 +132,9 @@ namespace Barotrauma
|
||||
case "crew":
|
||||
TagCrew();
|
||||
break;
|
||||
case "humanprefabidentifier":
|
||||
if (kvp.Length > 1) { TagHumansByIdentifier(kvp[1].Trim()); }
|
||||
break;
|
||||
case "structureidentifier":
|
||||
if (kvp.Length > 1) { TagStructuresByIdentifier(kvp[1].Trim()); }
|
||||
break;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -30,6 +31,9 @@ namespace Barotrauma
|
||||
[Serialize(true, true, description: "If true, dead/unconscious characters cannot trigger the action.")]
|
||||
public bool DisableIfTargetIncapacitated { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "If true, one target must interact with the other to trigger the action.")]
|
||||
public bool WaitForInteraction { get; set; }
|
||||
|
||||
private float distance;
|
||||
|
||||
public TriggerAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
|
||||
@@ -44,12 +48,15 @@ namespace Barotrauma
|
||||
}
|
||||
public override void Reset()
|
||||
{
|
||||
ResetTargetIcons();
|
||||
isRunning = false;
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
public bool isRunning = false;
|
||||
|
||||
private Either<Character, Item> npcOrItem = null;
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
@@ -81,20 +88,104 @@ namespace Barotrauma
|
||||
if (DisableInCombat && IsInCombat(e2)) { continue; }
|
||||
if (DisableIfTargetIncapacitated && e2 is Character character2 && (character2.IsDead || character2.IsIncapacitated)) { 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)
|
||||
if (WaitForInteraction)
|
||||
{
|
||||
Trigger(e1, e2);
|
||||
return;
|
||||
Character player = null;
|
||||
Character npc = null;
|
||||
Item item = null;
|
||||
npcOrItem?.TryGet(out npc);
|
||||
npcOrItem?.TryGet(out item);
|
||||
if (e1 is Character char1)
|
||||
{
|
||||
if (char1.IsBot) { npc ??= char1; }
|
||||
else { player = char1; }
|
||||
}
|
||||
else
|
||||
{
|
||||
item ??= e1 as Item;
|
||||
}
|
||||
if (e2 is Character char2)
|
||||
{
|
||||
if (char2.IsBot) { npc ??= char2; }
|
||||
else { player = char2; }
|
||||
}
|
||||
else
|
||||
{
|
||||
item ??= e2 as Item;
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
if (npc != null)
|
||||
{
|
||||
if (npc.CampaignInteractionType != CampaignMode.InteractionType.Examine)
|
||||
{
|
||||
npcOrItem = npc;
|
||||
npc.CampaignInteractionType = CampaignMode.InteractionType.Examine;
|
||||
npc.RequireConsciousnessForCustomInteract = false;
|
||||
#if CLIENT
|
||||
npc.SetCustomInteract(
|
||||
(speaker, player) => { if (e1 == speaker) { Trigger(speaker, player); } else { Trigger(player, speaker); } },
|
||||
TextManager.GetWithVariable("CampaignInteraction.Examine", "[key]", GameMain.Config.KeyBindText(InputType.Use)));
|
||||
#else
|
||||
npc.SetCustomInteract(
|
||||
(speaker, player) => { if (e1 == speaker) { Trigger(speaker, player); } else { Trigger(player, speaker); } },
|
||||
TextManager.Get("CampaignInteraction.Talk"));
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (item != null)
|
||||
{
|
||||
npcOrItem = item;
|
||||
item.CampaignInteractionType = CampaignMode.InteractionType.Examine;
|
||||
if (player.SelectedConstruction == item ||
|
||||
player.Inventory.Contains(item) ||
|
||||
(player.FocusedItem == item && player.IsKeyHit(InputType.Use)))
|
||||
{
|
||||
Trigger(e1, e2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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 void ResetTargetIcons()
|
||||
{
|
||||
if (npcOrItem == null) { return; }
|
||||
if (npcOrItem.TryGet(out Character npc))
|
||||
{
|
||||
npc.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||
npc.SetCustomInteract(null, null);
|
||||
npc.RequireConsciousnessForCustomInteract = true;
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
|
||||
#endif
|
||||
}
|
||||
else if (npcOrItem.TryGet(out Item item))
|
||||
{
|
||||
item.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsCloseEnoughToHull(Entity e, out Hull hull)
|
||||
{
|
||||
hull = null;
|
||||
@@ -157,6 +248,7 @@ namespace Barotrauma
|
||||
|
||||
private void Trigger(Entity entity1, Entity entity2)
|
||||
{
|
||||
ResetTargetIcons();
|
||||
if (!string.IsNullOrEmpty(ApplyToTarget1))
|
||||
{
|
||||
ParentEvent.AddTarget(ApplyToTarget1, entity1);
|
||||
@@ -174,7 +266,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (string.IsNullOrEmpty(TargetModuleType))
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished, isRunning)} {nameof(TriggerAction)} -> (Distance: {((int)distance).ColorizeObject()}, Radius: {Radius.ColorizeObject()}, TargetTags: {Target1Tag.ColorizeObject()}, {Target2Tag.ColorizeObject()})";
|
||||
return
|
||||
$"{ToolBox.GetDebugSymbol(isFinished, isRunning)} {nameof(TriggerAction)} -> (" +
|
||||
(WaitForInteraction ?
|
||||
$"Selected non-player target: {(npcOrItem?.ToString() ?? "<null>").ColorizeObject()}, " :
|
||||
$"Distance: {((int)distance).ColorizeObject()}, ") +
|
||||
$"Radius: {Radius.ColorizeObject()}, " +
|
||||
$"TargetTags: {Target1Tag.ColorizeObject()}, " +
|
||||
$"{Target2Tag.ColorizeObject()})";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user