v0.13.0.11
This commit is contained in:
@@ -109,7 +109,7 @@ namespace Barotrauma
|
||||
state = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (!Submarine.MainSub.AtEndPosition && !Submarine.MainSub.AtStartPosition) return;
|
||||
if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit) return;
|
||||
|
||||
Finished();
|
||||
state = 2;
|
||||
|
||||
+16
-17
@@ -27,25 +27,24 @@ namespace Barotrauma
|
||||
if (string.IsNullOrWhiteSpace(Identifier) || string.IsNullOrWhiteSpace(TargetTag)) { return false; }
|
||||
List<Character> targets = ParentEvent.GetTargets(TargetTag).OfType<Character>().ToList();
|
||||
|
||||
if (!(targets.FirstOrDefault() is { } target)) { return false; }
|
||||
|
||||
if (TargetLimb == LimbType.None)
|
||||
foreach (var target in targets)
|
||||
{
|
||||
Affliction? affliction = target.CharacterHealth?.GetAffliction(Identifier, AllowLimbAfflictions);
|
||||
return affliction != null;
|
||||
if (target.CharacterHealth == null) { continue; }
|
||||
if (TargetLimb == LimbType.None)
|
||||
{
|
||||
if (target.CharacterHealth.GetAffliction(Identifier, AllowLimbAfflictions) != null) { return true; }
|
||||
}
|
||||
IEnumerable<Affliction> afflictions = target.CharacterHealth.GetAllAfflictions().Where(affliction =>
|
||||
{
|
||||
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
|
||||
if (limbType == null) { return false; }
|
||||
|
||||
return limbType == TargetLimb || true;
|
||||
});
|
||||
|
||||
if (afflictions.Any(a => a.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase))) { return true; }
|
||||
}
|
||||
|
||||
if (target.CharacterHealth == null) { return false; }
|
||||
|
||||
IEnumerable<Affliction> afflictions = target.CharacterHealth.GetAllAfflictions().Where(affliction =>
|
||||
{
|
||||
LimbType? limbType = target.CharacterHealth.GetAfflictionLimb(affliction)?.type;
|
||||
if (limbType == null) { return false; }
|
||||
|
||||
return limbType == TargetLimb || true;
|
||||
});
|
||||
|
||||
return afflictions.Any(a => a.Identifier.Equals(Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
|
||||
+14
-24
@@ -61,7 +61,6 @@ namespace Barotrauma
|
||||
|
||||
private Character speaker;
|
||||
|
||||
private OrderInfo? prevSpeakerOrder;
|
||||
private AIObjective prevIdleObjective, prevGotoObjective;
|
||||
|
||||
public List<SubactionGroup> Options { get; private set; }
|
||||
@@ -180,6 +179,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (speaker == null) { return; }
|
||||
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||
speaker.ActiveConversation = this;
|
||||
speaker.SetCustomInteract(null, null);
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(speaker, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
|
||||
@@ -187,16 +187,10 @@ namespace Barotrauma
|
||||
var humanAI = speaker.AIController as HumanAIController;
|
||||
if (humanAI != null && !speaker.IsDead && !speaker.Removed)
|
||||
{
|
||||
if (prevSpeakerOrder != null)
|
||||
{
|
||||
humanAI.SetOrder(prevSpeakerOrder.Value.Order, prevSpeakerOrder.Value.OrderOption, orderGiver: null, speak: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
humanAI.SetOrder(null, string.Empty, orderGiver: null, speak: false);
|
||||
}
|
||||
humanAI.ClearForcedOrder();
|
||||
if (prevIdleObjective != null) { humanAI.ObjectiveManager.AddObjective(prevIdleObjective); }
|
||||
if (prevGotoObjective != null) { humanAI.ObjectiveManager.AddObjective(prevGotoObjective); }
|
||||
humanAI.ObjectiveManager.SortObjectives();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,24 +215,24 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
Character.DisableControls = true;
|
||||
#endif
|
||||
if (ShouldInterrupt())
|
||||
if (ShouldInterrupt())
|
||||
{
|
||||
ResetSpeaker();
|
||||
interrupt = true;
|
||||
interrupt = true;
|
||||
}
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(SpeakerTag))
|
||||
{
|
||||
if (speaker != null && !speaker.Removed && speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk) { return; }
|
||||
if (speaker != null && !speaker.Removed && speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk && speaker.ActiveConversation?.ParentEvent != this.ParentEvent) { return; }
|
||||
speaker = ParentEvent.GetTargets(SpeakerTag).FirstOrDefault(e => e is Character) as Character;
|
||||
if (speaker == null || speaker.Removed)
|
||||
{
|
||||
return;
|
||||
{
|
||||
return;
|
||||
}
|
||||
//some conversation already assigned to the speaker, wait for it to be removed
|
||||
if (speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk)
|
||||
if (speaker.CampaignInteractionType == CampaignMode.InteractionType.Talk && speaker.ActiveConversation?.ParentEvent != this.ParentEvent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -249,6 +243,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
speaker.CampaignInteractionType = CampaignMode.InteractionType.Talk;
|
||||
speaker.ActiveConversation = this;
|
||||
#if CLIENT
|
||||
speaker.SetCustomInteract(
|
||||
TryStartConversation,
|
||||
@@ -324,16 +319,11 @@ namespace Barotrauma
|
||||
|
||||
if (speaker?.AIController is HumanAIController humanAI)
|
||||
{
|
||||
prevSpeakerOrder = null;
|
||||
if (humanAI.CurrentOrder != null)
|
||||
{
|
||||
prevSpeakerOrder = new OrderInfo(humanAI.CurrentOrder, humanAI.CurrentOrderOption);
|
||||
}
|
||||
prevIdleObjective = humanAI.ObjectiveManager.GetObjective<AIObjectiveIdle>();
|
||||
prevGotoObjective = humanAI.ObjectiveManager.GetObjective<AIObjectiveGoTo>();
|
||||
humanAI.SetOrder(
|
||||
Order.PrefabList.Find(o => o.Identifier.Equals("wait", StringComparison.OrdinalIgnoreCase)),
|
||||
option: string.Empty, orderGiver: null, speak: false);
|
||||
humanAI.SetForcedOrder(
|
||||
Order.PrefabList.Find(o => o.Identifier.Equals("wait", StringComparison.OrdinalIgnoreCase)),
|
||||
option: string.Empty, orderGiver: null);
|
||||
if (targets.Any())
|
||||
{
|
||||
Entity closestTarget = null;
|
||||
|
||||
@@ -18,14 +18,13 @@ namespace Barotrauma
|
||||
|
||||
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.");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": 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.");
|
||||
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,30 +117,10 @@ namespace Barotrauma
|
||||
foreach (Item item in newCharacter.Inventory.AllItems)
|
||||
{
|
||||
item.SpawnedInOutpost = true;
|
||||
item.AllowStealing = false;
|
||||
}
|
||||
}
|
||||
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.Behavior;
|
||||
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));
|
||||
}
|
||||
}
|
||||
humanPrefab.InitializeCharacter(newCharacter, spawnPos);
|
||||
if (!string.IsNullOrEmpty(TargetTag) && newCharacter != null)
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newCharacter);
|
||||
@@ -261,7 +241,7 @@ namespace Barotrauma
|
||||
return GetSpawnPos(SpawnLocation, spawnPointType, targetModuleTags, SpawnPointTag.ToEnumerable());
|
||||
}
|
||||
|
||||
public static WayPoint GetSpawnPos(SpawnLocationType spawnLocation, SpawnType? spawnPointType, IEnumerable<string> moduleFlags = null, IEnumerable<string> spawnpointTags = null)
|
||||
public static WayPoint GetSpawnPos(SpawnLocationType spawnLocation, SpawnType? spawnPointType, IEnumerable<string> moduleFlags = null, IEnumerable<string> spawnpointTags = null, bool asFarAsPossibleFromAirlock = false)
|
||||
{
|
||||
List<WayPoint> potentialSpawnPoints = spawnLocation switch
|
||||
{
|
||||
@@ -275,6 +255,7 @@ namespace Barotrauma
|
||||
|
||||
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();
|
||||
@@ -303,7 +284,7 @@ namespace Barotrauma
|
||||
IEnumerable<WayPoint> validSpawnPoints;
|
||||
if (spawnPointType.HasValue)
|
||||
{
|
||||
validSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.SpawnType == spawnPointType.Value);
|
||||
validSpawnPoints = potentialSpawnPoints.FindAll(wp => spawnPointType.Value.HasFlag(wp.SpawnType));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -312,7 +293,6 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//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);
|
||||
@@ -324,6 +304,12 @@ namespace Barotrauma
|
||||
return potentialSpawnPoints.GetRandom();
|
||||
}
|
||||
|
||||
//avoid using waypoints if there's any actual spawnpoints available
|
||||
if (validSpawnPoints.Any(wp => wp.SpawnType != SpawnType.Path))
|
||||
{
|
||||
validSpawnPoints = validSpawnPoints.Where(wp => wp.SpawnType != SpawnType.Path);
|
||||
}
|
||||
|
||||
//if not trying to spawn at a tagged spawnpoint, favor spawnpoints without tags
|
||||
if (spawnpointTags == null || !spawnpointTags.Any())
|
||||
{
|
||||
@@ -334,7 +320,25 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
return validSpawnPoints.GetRandom();
|
||||
if (asFarAsPossibleFromAirlock && airlockSpawnPoints.Any())
|
||||
{
|
||||
WayPoint furthestPoint = validSpawnPoints.First();
|
||||
float furthestDist = 0.0f;
|
||||
foreach (WayPoint waypoint in validSpawnPoints)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(waypoint.WorldPosition, airlockSpawnPoints.First().WorldPosition);
|
||||
if (dist > furthestDist)
|
||||
{
|
||||
furthestDist = dist;
|
||||
furthestPoint = waypoint;
|
||||
}
|
||||
}
|
||||
return furthestPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
return validSpawnPoints.GetRandom();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
|
||||
@@ -6,12 +6,17 @@ namespace Barotrauma
|
||||
{
|
||||
class TagAction : EventAction
|
||||
{
|
||||
public enum SubType { Any= 0, Player = 1, Outpost = 2, Wreck = 4, BeaconStation = 8 }
|
||||
|
||||
[Serialize("", true)]
|
||||
public string Criteria { get; set; }
|
||||
|
||||
[Serialize("", true)]
|
||||
public string Tag { get; set; }
|
||||
|
||||
[Serialize(SubType.Any, true)]
|
||||
public SubType SubmarineType { get; set; }
|
||||
|
||||
[Serialize(true, true)]
|
||||
public bool IgnoreIncapacitatedCharacters { get; set; }
|
||||
|
||||
@@ -40,15 +45,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void TagBots()
|
||||
private void TagBots(bool playerCrewOnly)
|
||||
{
|
||||
if (IgnoreIncapacitatedCharacters)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot && !c.IsIncapacitated);
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot && !c.IsIncapacitated && (!playerCrewOnly || c.TeamID == CharacterTeamType.Team1));
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot);
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot && (!playerCrewOnly || c.TeamID == CharacterTeamType.Team1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,23 +62,44 @@ namespace Barotrauma
|
||||
#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
|
||||
TagPlayers();
|
||||
TagBots(playerCrewOnly: true);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void TagStructuresByIdentifier(string identifier)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && s.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Structure s && SubmarineTypeMatches(s.Submarine) && 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));
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && SubmarineTypeMatches(it.Submarine) && it.Prefab.Identifier.Equals(identifier, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
private void TagItemsByTag(string tag)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && it.HasTag(tag));
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && SubmarineTypeMatches(it.Submarine) && it.HasTag(tag));
|
||||
}
|
||||
|
||||
private bool SubmarineTypeMatches(Submarine sub)
|
||||
{
|
||||
if (SubmarineType == SubType.Any) { return true; }
|
||||
if (sub == null) { return false; }
|
||||
switch (sub.Info.Type)
|
||||
{
|
||||
case Barotrauma.SubmarineType.Player:
|
||||
return SubmarineType.HasFlag(SubType.Player);
|
||||
case Barotrauma.SubmarineType.Outpost:
|
||||
case Barotrauma.SubmarineType.OutpostModule:
|
||||
return SubmarineType.HasFlag(SubType.Outpost);
|
||||
case Barotrauma.SubmarineType.Wreck:
|
||||
return SubmarineType.HasFlag(SubType.Wreck);
|
||||
case Barotrauma.SubmarineType.BeaconStation:
|
||||
return SubmarineType.HasFlag(SubType.BeaconStation);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
@@ -91,7 +117,7 @@ namespace Barotrauma
|
||||
TagPlayers();
|
||||
break;
|
||||
case "bot":
|
||||
TagBots();
|
||||
TagBots(playerCrewOnly: false);
|
||||
break;
|
||||
case "crew":
|
||||
TagCrew();
|
||||
@@ -113,7 +139,7 @@ namespace Barotrauma
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()})";
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(TagAction)} -> (Criteria: {Criteria.ColorizeObject()}, Tag: {Tag.ColorizeObject()}, Sub: {SubmarineType.ColorizeObject()})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class UnlockPathAction : EventAction
|
||||
{
|
||||
public UnlockPathAction(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?.Map?.CurrentLocation?.Connections != null)
|
||||
{
|
||||
foreach (LocationConnection connection in GameMain.GameSession?.Map?.CurrentLocation?.Connections)
|
||||
{
|
||||
if (!connection.Locked) { continue; }
|
||||
connection.Locked = false;
|
||||
#if SERVER
|
||||
NotifyUnlock(connection);
|
||||
#else
|
||||
new GUIMessageBox(string.Empty, TextManager.Get("pathunlockedgeneric"),
|
||||
new string[0], type: GUIMessageBox.Type.InGame, iconStyle: "UnlockPathIcon", relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(UnlockPathAction)}";
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
private void NotifyUnlock(LocationConnection connection)
|
||||
{
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
|
||||
outmsg.Write((byte)EventManager.NetworkEventType.UNLOCKPATH);
|
||||
outmsg.Write((UInt16)GameMain.GameSession.Map.Connections.IndexOf(connection));
|
||||
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using NLog;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -13,7 +14,8 @@ namespace Barotrauma
|
||||
{
|
||||
CONVERSATION,
|
||||
STATUSEFFECT,
|
||||
MISSION
|
||||
MISSION,
|
||||
UNLOCKPATH
|
||||
}
|
||||
|
||||
const float IntensityUpdateInterval = 5.0f;
|
||||
@@ -93,6 +95,8 @@ namespace Barotrauma
|
||||
|
||||
public void StartRound(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
|
||||
if (isClient) { return; }
|
||||
|
||||
pendingEventSets.Clear();
|
||||
@@ -107,24 +111,52 @@ namespace Barotrauma
|
||||
totalPathLength = steeringPath.TotalLength;
|
||||
}
|
||||
|
||||
this.level = level;
|
||||
SelectSettings();
|
||||
|
||||
int seed = 0;
|
||||
if (level != null)
|
||||
{
|
||||
seed = ToolBox.StringToInt(level.Seed);
|
||||
foreach (var previousEvent in level.LevelData.EventHistory)
|
||||
{
|
||||
seed ^= ToolBox.StringToInt(previousEvent.Identifier);
|
||||
}
|
||||
}
|
||||
MTRandom rand = new MTRandom(seed);
|
||||
|
||||
var initialEventSet = SelectRandomEvents(EventSet.List);
|
||||
if (initialEventSet != null)
|
||||
{
|
||||
pendingEventSets.Add(initialEventSet);
|
||||
int seed = ToolBox.StringToInt(level.Seed);
|
||||
foreach (var previousEvent in level.LevelData.EventHistory)
|
||||
{
|
||||
seed ^= ToolBox.StringToInt(previousEvent.Identifier);
|
||||
}
|
||||
MTRandom rand = new MTRandom(seed);
|
||||
CreateEvents(initialEventSet, rand);
|
||||
}
|
||||
|
||||
if (level?.LevelData?.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
//if the outpost is connected to a locked connection, create an event to unlock it
|
||||
if (level.StartLocation?.Connections.Any(c => c.Locked && level.StartLocation.MapPosition.X < c.OtherLocation(level.StartLocation).MapPosition.X) ?? false)
|
||||
{
|
||||
var unlockPathPrefabs = EventSet.PrefabList.FindAll(e => e.UnlockPathEvent);
|
||||
var unlockPathPrefabsForBiome = unlockPathPrefabs.FindAll(e =>
|
||||
string.IsNullOrEmpty(e.BiomeIdentifier) ||
|
||||
e.BiomeIdentifier.Equals(level.LevelData.Biome.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var unlockPathEventPrefab = unlockPathPrefabsForBiome.Any() ?
|
||||
ToolBox.SelectWeightedRandom(unlockPathPrefabsForBiome, unlockPathPrefabsForBiome.Select(b => b.Commonness).ToList(), rand) :
|
||||
ToolBox.SelectWeightedRandom(unlockPathPrefabs, unlockPathPrefabs.Select(b => b.Commonness).ToList(), rand);
|
||||
if (unlockPathEventPrefab != null)
|
||||
{
|
||||
var newEvent = unlockPathEventPrefab.CreateInstance();
|
||||
newEvent.Init(true);
|
||||
ActiveEvents.Add(newEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if no event that unlocks the path can be found, unlock it automatically
|
||||
level.StartLocation.Connections.ForEach(c => c.Locked = false);
|
||||
}
|
||||
}
|
||||
|
||||
level.LevelData.EventHistory.AddRange(selectedEvents.Values.SelectMany(v => v).Select(e => e.Prefab).Where(e => !level.LevelData.EventHistory.Contains(e)));
|
||||
if (level.LevelData.EventHistory.Count > MaxEventHistory)
|
||||
{
|
||||
@@ -134,11 +166,14 @@ namespace Barotrauma
|
||||
void AddChildEvents(EventSet eventSet)
|
||||
{
|
||||
if (eventSet == null) { return; }
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First))
|
||||
if (eventSet.OncePerOutpost)
|
||||
{
|
||||
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First))
|
||||
{
|
||||
level.LevelData.NonRepeatableEvents.Add(ep);
|
||||
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
|
||||
{
|
||||
level.LevelData.NonRepeatableEvents.Add(ep);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (EventSet childSet in eventSet.ChildSets)
|
||||
@@ -286,14 +321,23 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
RagdollParams ragdollParams;
|
||||
if (humanoid)
|
||||
try
|
||||
{
|
||||
ragdollParams = RagdollParams.GetRagdollParams<HumanRagdollParams>(speciesName);
|
||||
if (humanoid)
|
||||
{
|
||||
ragdollParams = RagdollParams.GetRagdollParams<HumanRagdollParams>(characterPrefab.VariantOf ?? speciesName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ragdollParams = RagdollParams.GetRagdollParams<FishRagdollParams>(characterPrefab.VariantOf ?? speciesName);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
ragdollParams = RagdollParams.GetRagdollParams<FishRagdollParams>(speciesName);
|
||||
DebugConsole.ThrowError($"Failed to preload a ragdoll file for the character \"{characterPrefab.Name}\"", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ragdollParams != null)
|
||||
{
|
||||
HashSet<string> texturePaths = new HashSet<string>
|
||||
@@ -341,6 +385,8 @@ namespace Barotrauma
|
||||
private void CreateEvents(EventSet eventSet, Random rand)
|
||||
{
|
||||
if (level == null) { return; }
|
||||
if (level.LevelData.HasHuntingGrounds && eventSet.DisableInHuntingGrounds) { return; }
|
||||
|
||||
int applyCount = 1;
|
||||
List<Func<Level.InterestingPosition, bool>> spawnPosFilter = new List<Func<Level.InterestingPosition, bool>>();
|
||||
if (eventSet.PerRuin)
|
||||
@@ -361,22 +407,27 @@ namespace Barotrauma
|
||||
}
|
||||
else if (eventSet.PerWreck)
|
||||
{
|
||||
var wrecks = Submarine.Loaded.Where(s => s.Info.IsWreck && (s.WreckAI == null || !s.WreckAI.IsAlive));
|
||||
var wrecks = Submarine.Loaded.Where(s => s.Info.IsWreck && (s.WreckAI == null || !s.WreckAI.IsAlive));
|
||||
applyCount = wrecks.Count();
|
||||
foreach (var wreck in wrecks)
|
||||
{
|
||||
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Submarine == wreck; });
|
||||
}
|
||||
}
|
||||
|
||||
var suitablePrefabs = eventSet.EventPrefabs.FindAll(e =>
|
||||
string.IsNullOrEmpty(e.First.BiomeIdentifier) ||
|
||||
e.First.BiomeIdentifier.Equals(Level.Loaded.LevelData?.Biome?.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
{
|
||||
if (eventSet.EventPrefabs.Count > 0)
|
||||
if (suitablePrefabs.Count > 0)
|
||||
{
|
||||
List<Pair<EventPrefab, float>> unusedEvents = new List<Pair<EventPrefab, float>>(eventSet.EventPrefabs);
|
||||
List<Pair<EventPrefab, float>> unusedEvents = new List<Pair<EventPrefab, float>>(suitablePrefabs);
|
||||
for (int j = 0; j < eventSet.EventCount; j++)
|
||||
{
|
||||
if (unusedEvents.All(e => CalculateCommonness(e) <= 0.0f)) { break; }
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => CalculateCommonness(e)).ToList(), rand);
|
||||
if (eventPrefab != null)
|
||||
{
|
||||
@@ -402,7 +453,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Pair<EventPrefab, float> eventPrefab in eventSet.EventPrefabs)
|
||||
foreach (Pair<EventPrefab, float> eventPrefab in suitablePrefabs)
|
||||
{
|
||||
var newEvent = eventPrefab.First.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
@@ -429,11 +480,19 @@ namespace Barotrauma
|
||||
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
|
||||
|
||||
var allowedEventSets =
|
||||
eventSets.Where(es => level.Difficulty >= es.MinLevelDifficulty && level.Difficulty <= es.MaxLevelDifficulty && level.LevelData.Type == es.LevelType);
|
||||
eventSets.Where(es =>
|
||||
level.Difficulty >= es.MinLevelDifficulty && level.Difficulty <= es.MaxLevelDifficulty &&
|
||||
level.LevelData.Type == es.LevelType &&
|
||||
(string.IsNullOrEmpty(es.BiomeIdentifier) || es.BiomeIdentifier.Equals(level.LevelData.Biome.Identifier, StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
Location location = (GameMain.GameSession?.GameMode as CampaignMode)?.Map?.CurrentLocation ?? level?.StartLocation;
|
||||
LocationType locationType = location?.GetLocationType();
|
||||
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.Map?.CurrentLocation?.Type != null)
|
||||
if (location != null)
|
||||
{
|
||||
allowedEventSets = allowedEventSets.Where(set => set.LocationTypeIdentifiers == null || set.LocationTypeIdentifiers.Any(identifier => string.Equals(identifier, campaign.Map.CurrentLocation.Type.Identifier, StringComparison.OrdinalIgnoreCase)));
|
||||
allowedEventSets = allowedEventSets.Where(set =>
|
||||
set.LocationTypeIdentifiers == null ||
|
||||
set.LocationTypeIdentifiers.Any(identifier => string.Equals(identifier, locationType.Identifier, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
float totalCommonness = allowedEventSets.Sum(e => e.GetCommonness(level));
|
||||
@@ -454,8 +513,8 @@ namespace Barotrauma
|
||||
private bool CanStartEventSet(EventSet eventSet)
|
||||
{
|
||||
ISpatialEntity refEntity = GetRefEntity();
|
||||
float distFromStart = Vector2.Distance(refEntity.WorldPosition, level.StartPosition);
|
||||
float distFromEnd = Vector2.Distance(refEntity.WorldPosition, level.EndPosition);
|
||||
float distFromStart = (float)Math.Sqrt(MathUtils.LineSegmentToPointDistanceSquared(level.StartExitPosition.ToPoint(), level.StartPosition.ToPoint(), refEntity.WorldPosition.ToPoint()));
|
||||
float distFromEnd = (float)Math.Sqrt(MathUtils.LineSegmentToPointDistanceSquared(level.EndExitPosition.ToPoint(), level.EndPosition.ToPoint(), refEntity.WorldPosition.ToPoint()));
|
||||
|
||||
//don't create new events if within 50 meters of the start/end of the level
|
||||
if (!eventSet.AllowAtStart)
|
||||
|
||||
@@ -7,19 +7,22 @@ namespace Barotrauma
|
||||
class EventPrefab
|
||||
{
|
||||
public readonly XElement ConfigElement;
|
||||
public readonly Type EventType;
|
||||
public readonly string MusicType;
|
||||
public readonly Type EventType;
|
||||
public readonly float SpawnProbability;
|
||||
public readonly bool TriggerEventCooldown;
|
||||
public float Commonness;
|
||||
public string Identifier;
|
||||
public string BiomeIdentifier;
|
||||
|
||||
public bool UnlockPathEvent;
|
||||
public string UnlockPathTooltip;
|
||||
public int UnlockPathReputation;
|
||||
public string UnlockPathFaction;
|
||||
|
||||
public EventPrefab(XElement element)
|
||||
{
|
||||
ConfigElement = element;
|
||||
|
||||
MusicType = element.GetAttributeString("musictype", "default");
|
||||
|
||||
try
|
||||
{
|
||||
EventType = Type.GetType("Barotrauma." + ConfigElement.Name, true, true);
|
||||
@@ -34,9 +37,15 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Identifier = ConfigElement.GetAttributeString("identifier", string.Empty);
|
||||
BiomeIdentifier = ConfigElement.GetAttributeString("biome", string.Empty);
|
||||
Commonness = element.GetAttributeFloat("commonness", 1.0f);
|
||||
SpawnProbability = Math.Clamp(element.GetAttributeFloat("spawnprobability", 1.0f), 0, 1);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||
|
||||
UnlockPathEvent = element.GetAttributeBool("unlockpathevent", false);
|
||||
UnlockPathTooltip = element.GetAttributeString("unlockpathtooltip", "lockedpathtooltip");
|
||||
UnlockPathReputation = element.GetAttributeInt("unlockpathreputation", 0);
|
||||
UnlockPathFaction = element.GetAttributeString("unlockpathfaction", "");
|
||||
}
|
||||
|
||||
public Event CreateInstance()
|
||||
@@ -57,5 +66,10 @@ namespace Barotrauma
|
||||
|
||||
return (Event)instance;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"EventPrefab ({Identifier})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,8 @@ namespace Barotrauma
|
||||
//0-100
|
||||
public readonly float MinLevelDifficulty, MaxLevelDifficulty;
|
||||
|
||||
public readonly string BiomeIdentifier;
|
||||
|
||||
public readonly LevelData.LevelType LevelType;
|
||||
|
||||
public readonly string[] LocationTypeIdentifiers;
|
||||
@@ -84,6 +86,7 @@ namespace Barotrauma
|
||||
public readonly bool IgnoreCoolDown;
|
||||
|
||||
public readonly bool PerRuin, PerCave, PerWreck;
|
||||
public readonly bool DisableInHuntingGrounds;
|
||||
|
||||
public readonly bool OncePerOutpost;
|
||||
|
||||
@@ -111,6 +114,7 @@ namespace Barotrauma
|
||||
EventPrefabs = new List<Pair<EventPrefab, float>>();
|
||||
ChildSets = new List<EventSet>();
|
||||
|
||||
BiomeIdentifier = element.GetAttributeString("biome", string.Empty);
|
||||
MinLevelDifficulty = element.GetAttributeFloat("minleveldifficulty", 0);
|
||||
MaxLevelDifficulty = Math.Max(element.GetAttributeFloat("maxleveldifficulty", 100), MinLevelDifficulty);
|
||||
|
||||
@@ -139,9 +143,10 @@ namespace Barotrauma
|
||||
PerRuin = element.GetAttributeBool("perruin", false);
|
||||
PerCave = element.GetAttributeBool("percave", false);
|
||||
PerWreck = element.GetAttributeBool("perwreck", false);
|
||||
DisableInHuntingGrounds = element.GetAttributeBool("disableinhuntinggrounds", false);
|
||||
IgnoreCoolDown = element.GetAttributeBool("ignorecooldown", parentSet?.IgnoreCoolDown ?? (PerRuin || PerCave || PerWreck));
|
||||
DelayWhenCrewAway = element.GetAttributeBool("delaywhencrewaway", !PerRuin && !PerCave && !PerWreck);
|
||||
OncePerOutpost = element.GetAttributeBool("perwreck", false);
|
||||
OncePerOutpost = element.GetAttributeBool("onceperoutpost", false);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||
|
||||
Commonness[""] = 1.0f;
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
using Barotrauma.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AbandonedOutpostMission : Mission
|
||||
{
|
||||
private readonly XElement characterConfig;
|
||||
|
||||
protected readonly List<Character> characters = new List<Character>();
|
||||
private readonly Dictionary<Character, List<Item>> characterItems = new Dictionary<Character, List<Item>>();
|
||||
protected readonly HashSet<Character> requireKill = new HashSet<Character>();
|
||||
protected readonly HashSet<Character> requireRescue = new HashSet<Character>();
|
||||
|
||||
protected const int HostagesKilledState = 5;
|
||||
|
||||
private readonly string hostagesKilledMessage;
|
||||
|
||||
private const float EndDelay = 5.0f;
|
||||
private float endTimer;
|
||||
|
||||
public override bool AllowRespawn => false;
|
||||
|
||||
public override bool AllowUndocking
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.GameSession.GameMode is CampaignMode) { return true; }
|
||||
return state > 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool wasDocked;
|
||||
|
||||
public AbandonedOutpostMission(MissionPrefab prefab, Location[] locations) :
|
||||
base(prefab, locations)
|
||||
{
|
||||
characterConfig = prefab.ConfigElement.Element("Characters");
|
||||
|
||||
string msgTag = prefab.ConfigElement.GetAttributeString("hostageskilledmessage", "");
|
||||
hostagesKilledMessage = TextManager.Get(msgTag, returnNull: true) ?? msgTag;
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
failed = false;
|
||||
endTimer = 0.0f;
|
||||
characters.Clear();
|
||||
characterItems.Clear();
|
||||
requireKill.Clear();
|
||||
requireRescue.Clear();
|
||||
|
||||
var submarine = Submarine.Loaded.Find(s => s.Info.Type == SubmarineType.Outpost) ?? Submarine.MainSub;
|
||||
if (!IsClient)
|
||||
{
|
||||
InitCharacters(submarine);
|
||||
}
|
||||
|
||||
wasDocked = Submarine.MainSub.DockedTo.Contains(Level.Loaded.StartOutpost);
|
||||
}
|
||||
|
||||
private void InitCharacters(Submarine submarine)
|
||||
{
|
||||
characters.Clear();
|
||||
characterItems.Clear();
|
||||
|
||||
if (characterConfig == null) { return; }
|
||||
|
||||
foreach (XElement element in characterConfig.Elements())
|
||||
{
|
||||
if (GameMain.NetworkMember == null && element.GetAttributeBool("multiplayeronly", false)) { continue; }
|
||||
|
||||
int defaultCount = element.GetAttributeInt("count", -1);
|
||||
if (defaultCount < 0)
|
||||
{
|
||||
defaultCount = element.GetAttributeInt("amount", 1);
|
||||
}
|
||||
int min = Math.Min(element.GetAttributeInt("min", defaultCount), 255);
|
||||
int max = Math.Min(Math.Max(min, element.GetAttributeInt("max", defaultCount)), 255);
|
||||
int count = Rand.Range(min, max + 1);
|
||||
|
||||
if (element.Attribute("identifier") != null && element.Attribute("from") != null)
|
||||
{
|
||||
string characterIdentifier = element.GetAttributeString("identifier", "");
|
||||
string characterFrom = element.GetAttributeString("from", "");
|
||||
HumanPrefab humanPrefab = NPCSet.Get(characterFrom, characterIdentifier);
|
||||
if (humanPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn a character for abandoned outpost mission: character prefab \"" + characterIdentifier + "\" not found");
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
LoadHuman(humanPrefab, element, submarine);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string speciesName = element.GetAttributeString("character", element.GetAttributeString("identifier", ""));
|
||||
var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName);
|
||||
if (characterPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn a character for abandoned outpost mission: character prefab \"" + speciesName + "\" not found");
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
LoadMonster(characterPrefab, element, submarine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadHuman(HumanPrefab humanPrefab, XElement element, Submarine submarine)
|
||||
{
|
||||
string[] moduleFlags = element.GetAttributeStringArray("moduleflags", null);
|
||||
string[] spawnPointTags = element.GetAttributeStringArray("spawnpointtags", null);
|
||||
ISpatialEntity spawnPos = SpawnAction.GetSpawnPos(
|
||||
SpawnAction.SpawnLocationType.Outpost, SpawnType.Human,
|
||||
moduleFlags ?? humanPrefab.GetModuleFlags(),
|
||||
spawnPointTags ?? humanPrefab.GetSpawnPointTags(),
|
||||
element.GetAttributeBool("asfaraspossible", false));
|
||||
if (spawnPos == null)
|
||||
{
|
||||
spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandom();
|
||||
}
|
||||
|
||||
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server);
|
||||
Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, spawnPos.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false);
|
||||
if (element.GetAttributeBool("requirerescue", false))
|
||||
{
|
||||
requireRescue.Add(spawnedCharacter);
|
||||
spawnedCharacter.TeamID = CharacterTeamType.FriendlyNPC;
|
||||
#if CLIENT
|
||||
GameMain.GameSession.CrewManager.AddCharacterToCrewList(spawnedCharacter);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnedCharacter.TeamID = CharacterTeamType.None;
|
||||
}
|
||||
humanPrefab.InitializeCharacter(spawnedCharacter, spawnPos);
|
||||
humanPrefab.GiveItems(spawnedCharacter, Submarine.MainSub, Rand.RandSync.Server, createNetworkEvents: false);
|
||||
if (spawnPos is WayPoint wp)
|
||||
{
|
||||
spawnedCharacter.GiveIdCardTags(wp);
|
||||
}
|
||||
if (element.GetAttributeBool("requirekill", false))
|
||||
{
|
||||
requireKill.Add(spawnedCharacter);
|
||||
}
|
||||
characters.Add(spawnedCharacter);
|
||||
characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true));
|
||||
}
|
||||
|
||||
private void LoadMonster(CharacterPrefab monsterPrefab, XElement element, Submarine submarine)
|
||||
{
|
||||
string[] moduleFlags = element.GetAttributeStringArray("moduleflags", null);
|
||||
string[] spawnPointTags = element.GetAttributeStringArray("spawnpointtags", null);
|
||||
ISpatialEntity spawnPos = SpawnAction.GetSpawnPos(SpawnAction.SpawnLocationType.Outpost, SpawnType.Enemy, moduleFlags, spawnPointTags, element.GetAttributeBool("asfaraspossible", false));
|
||||
if (spawnPos == null)
|
||||
{
|
||||
spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandom();
|
||||
}
|
||||
Character spawnedCharacter = Character.Create(monsterPrefab.Identifier, spawnPos.WorldPosition, ToolBox.RandomSeed(8), createNetworkEvent: false);
|
||||
characters.Add(spawnedCharacter);
|
||||
if (element.GetAttributeBool("requirekill", false))
|
||||
{
|
||||
requireKill.Add(spawnedCharacter);
|
||||
}
|
||||
if (spawnedCharacter.Inventory != null)
|
||||
{
|
||||
characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true));
|
||||
}
|
||||
if (submarine != null && spawnedCharacter.AIController is EnemyAIController enemyAi)
|
||||
{
|
||||
enemyAi.UnattackableSubmarines.Add(submarine);
|
||||
enemyAi.UnattackableSubmarines.Add(Submarine.MainSub);
|
||||
foreach (Submarine sub in Submarine.MainSub.DockedTo)
|
||||
{
|
||||
enemyAi.UnattackableSubmarines.Add(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (State != HostagesKilledState)
|
||||
{
|
||||
if (requireRescue.Any(r => r.Removed || r.IsDead))
|
||||
{
|
||||
State = HostagesKilledState;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
endTimer += deltaTime;
|
||||
if (endTimer > EndDelay)
|
||||
{
|
||||
#if SERVER
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode) && GameMain.Server != null)
|
||||
{
|
||||
GameMain.Server.EndGame();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
|
||||
if (requireKill.All(c => c.Removed || c.IsDead) &&
|
||||
requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player))
|
||||
{
|
||||
State = 1;
|
||||
}
|
||||
break;
|
||||
#if SERVER
|
||||
case 1:
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode) && GameMain.Server != null)
|
||||
{
|
||||
if (!Submarine.MainSub.AtStartExit || (wasDocked && !Submarine.MainSub.DockedTo.Contains(Level.Loaded.StartOutpost)))
|
||||
{
|
||||
GameMain.Server.EndGame();
|
||||
State = 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
completed = State > 0 && State != HostagesKilledState;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
}
|
||||
else
|
||||
{
|
||||
failed = requireRescue.Any(r => r.Removed || r.IsDead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ namespace Barotrauma
|
||||
private bool swarmSpawned;
|
||||
private readonly string monsterSpeciesName;
|
||||
private Point monsterCountRange;
|
||||
private Level level;
|
||||
private readonly string sonarLabel;
|
||||
|
||||
public BeaconMission(MissionPrefab prefab, Location[] locations) : base(prefab, locations)
|
||||
@@ -54,11 +53,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (IsClient) { return; }
|
||||
@@ -113,8 +107,15 @@ namespace Barotrauma
|
||||
completed = level.CheckBeaconActive();
|
||||
if (completed)
|
||||
{
|
||||
ChangeLocationType("None", "Explored");
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
if (level?.LevelData != null)
|
||||
{
|
||||
level.LevelData.IsBeaconActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ namespace Barotrauma
|
||||
|
||||
var item = new Item(itemPrefab, position, cargoRoom.Submarine)
|
||||
{
|
||||
SpawnedInOutpost = true
|
||||
SpawnedInOutpost = true,
|
||||
AllowStealing = false
|
||||
};
|
||||
item.FindHull();
|
||||
items.Add(item);
|
||||
@@ -118,7 +119,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
items.Clear();
|
||||
parentInventoryIDs.Clear();
|
||||
@@ -131,13 +132,17 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (Submarine.MainSub != null && Submarine.MainSub.AtEndPosition)
|
||||
if (Submarine.MainSub != null && Submarine.MainSub.AtEndExit)
|
||||
{
|
||||
int deliveredItemCount = items.Count(i => i.CurrentHull != null && !i.Removed && i.Condition > 0.0f);
|
||||
if (deliveredItemCount >= requiredDeliveryAmount)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -89,8 +90,8 @@ namespace Barotrauma
|
||||
Winner != CharacterTeamType.None &&
|
||||
Winner == character.TeamID;
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (GameMain.NetworkMember == null)
|
||||
{
|
||||
@@ -99,23 +100,23 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
subs = new Submarine[] { Submarine.MainSubs[0], Submarine.MainSubs[1] };
|
||||
subs[0].TeamID = CharacterTeamType.Team1; subs[1].TeamID = CharacterTeamType.Team2;
|
||||
subs[0].NeutralizeBallast(); subs[1].NeutralizeBallast();
|
||||
|
||||
subs[0].NeutralizeBallast();
|
||||
subs[0].TeamID = CharacterTeamType.Team1;
|
||||
subs[0].DockedTo.ForEach(s => s.TeamID = CharacterTeamType.Team1);
|
||||
|
||||
subs[1].NeutralizeBallast();
|
||||
subs[1].TeamID = CharacterTeamType.Team2;
|
||||
subs[1].DockedTo.ForEach(s => s.TeamID = CharacterTeamType.Team2);
|
||||
subs[1].SetPosition(subs[1].FindSpawnPos(Level.Loaded.EndPosition));
|
||||
subs[1].FlipX();
|
||||
|
||||
crews = new List<Character>[] { new List<Character>(), new List<Character>() };
|
||||
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
//hide all subs from sonar to make sneak attacks possible
|
||||
submarine.ShowSonarMarker = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (GameMain.NetworkMember == null) return;
|
||||
if (GameMain.NetworkMember == null) { return; }
|
||||
|
||||
if (Winner != CharacterTeamType.None)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (SpawnedResources.Any())
|
||||
{
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma
|
||||
State = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (!Submarine.MainSub.AtEndPosition && !Submarine.MainSub.AtStartPosition) { return; }
|
||||
if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit) { return; }
|
||||
State = 2;
|
||||
break;
|
||||
}
|
||||
@@ -135,6 +135,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (EnoughHaveBeenCollected())
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -11,8 +10,11 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly MissionPrefab Prefab;
|
||||
protected bool completed, failed;
|
||||
|
||||
protected Level level;
|
||||
|
||||
protected int state;
|
||||
public int State
|
||||
public virtual int State
|
||||
{
|
||||
get { return state; }
|
||||
protected set
|
||||
@@ -21,7 +23,7 @@ namespace Barotrauma
|
||||
{
|
||||
state = value;
|
||||
#if SERVER
|
||||
GameMain.Server?.UpdateMissionState(state);
|
||||
GameMain.Server?.UpdateMissionState(this, state);
|
||||
#endif
|
||||
ShowMessage(State);
|
||||
}
|
||||
@@ -38,25 +40,30 @@ namespace Barotrauma
|
||||
get { return Prefab.Name; }
|
||||
}
|
||||
|
||||
private string successMessage;
|
||||
private readonly string successMessage;
|
||||
public virtual string SuccessMessage
|
||||
{
|
||||
get { return successMessage; }
|
||||
private set { successMessage = value; }
|
||||
//private set { successMessage = value; }
|
||||
}
|
||||
|
||||
private string failureMessage;
|
||||
private readonly string failureMessage;
|
||||
public virtual string FailureMessage
|
||||
{
|
||||
get { return failureMessage; }
|
||||
private set { failureMessage = value; }
|
||||
//private set { failureMessage = value; }
|
||||
}
|
||||
|
||||
protected string description;
|
||||
public virtual string Description
|
||||
{
|
||||
get { return description; }
|
||||
private set { description = value; }
|
||||
//private set { description = value; }
|
||||
}
|
||||
|
||||
public virtual bool AllowUndocking
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public int Reward
|
||||
@@ -100,6 +107,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public readonly Location[] Locations;
|
||||
|
||||
public int? Difficulty
|
||||
{
|
||||
get { return Prefab.Difficulty; }
|
||||
}
|
||||
|
||||
public Mission(MissionPrefab prefab, Location[] locations)
|
||||
{
|
||||
@@ -109,7 +121,7 @@ namespace Barotrauma
|
||||
|
||||
description = prefab.Description;
|
||||
successMessage = prefab.SuccessMessage;
|
||||
FailureMessage = prefab.FailureMessage;
|
||||
failureMessage = prefab.FailureMessage;
|
||||
Headers = new List<string>(prefab.Headers);
|
||||
Messages = new List<string>(prefab.Messages);
|
||||
|
||||
@@ -117,20 +129,22 @@ namespace Barotrauma
|
||||
|
||||
for (int n = 0; n < 2; n++)
|
||||
{
|
||||
if (description != null) description = description.Replace("[location" + (n + 1) + "]", locations[n].Name);
|
||||
if (successMessage != null) successMessage = successMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
|
||||
if (failureMessage != null) failureMessage = failureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
|
||||
string locationName = $"‖color:gui.orange‖{locations[n].Name}‖end‖";
|
||||
if (description != null) { description = description.Replace("[location" + (n + 1) + "]", locationName); }
|
||||
if (successMessage != null) { successMessage = successMessage.Replace("[location" + (n + 1) + "]", locationName); }
|
||||
if (failureMessage != null) { failureMessage = failureMessage.Replace("[location" + (n + 1) + "]", locationName); }
|
||||
for (int m = 0; m < Messages.Count; m++)
|
||||
{
|
||||
Messages[m] = Messages[m].Replace("[location" + (n + 1) + "]", locations[n].Name);
|
||||
Messages[m] = Messages[m].Replace("[location" + (n + 1) + "]", locationName);
|
||||
}
|
||||
}
|
||||
if (description != null) description = description.Replace("[reward]", Reward.ToString("N0"));
|
||||
if (successMessage != null) successMessage = successMessage.Replace("[reward]", Reward.ToString("N0"));
|
||||
if (failureMessage != null) failureMessage = failureMessage.Replace("[reward]", Reward.ToString("N0"));
|
||||
string rewardText = $"‖color:gui.orange‖{string.Format(CultureInfo.InvariantCulture, "{0:N0}", Reward)}‖end‖";
|
||||
if (description != null) { description = description.Replace("[reward]", rewardText); }
|
||||
if (successMessage != null) { successMessage = successMessage.Replace("[reward]", rewardText); }
|
||||
if (failureMessage != null) { failureMessage = failureMessage.Replace("[reward]", rewardText); }
|
||||
for (int m = 0; m < Messages.Count; m++)
|
||||
{
|
||||
Messages[m] = Messages[m].Replace("[reward]", Reward.ToString("N0"));
|
||||
Messages[m] = Messages[m].Replace("[reward]", rewardText);
|
||||
}
|
||||
}
|
||||
public static Mission LoadRandom(Location[] locations, string seed, bool requireCorrectLocationType, MissionType missionType, bool isSinglePlayer = false)
|
||||
@@ -175,7 +189,23 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void Start(Level level) { }
|
||||
public void Start(Level level)
|
||||
{
|
||||
#if CLIENT
|
||||
shownMessages.Clear();
|
||||
#endif
|
||||
foreach (string categoryToShow in Prefab.UnhideEntitySubCategories)
|
||||
{
|
||||
foreach (MapEntity entityToShow in MapEntity.mapEntityList.Where(me => me.prefab?.HasSubCategory(categoryToShow) ?? false))
|
||||
{
|
||||
entityToShow.HiddenInGame = false;
|
||||
}
|
||||
}
|
||||
this.level = level;
|
||||
StartMissionSpecific(level);
|
||||
}
|
||||
|
||||
protected virtual void StartMissionSpecific(Level level) { }
|
||||
|
||||
public virtual void Update(float deltaTime) { }
|
||||
|
||||
@@ -192,7 +222,10 @@ namespace Barotrauma
|
||||
public virtual void End()
|
||||
{
|
||||
completed = true;
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
}
|
||||
|
||||
@@ -224,22 +257,32 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected void ChangeLocationType(string from, string to)
|
||||
protected void ChangeLocationType(LocationTypeChange change)
|
||||
{
|
||||
if (change == null) { throw new ArgumentException(); }
|
||||
if (GameMain.GameSession.GameMode is CampaignMode && !IsClient)
|
||||
{
|
||||
int srcIndex = -1;
|
||||
for (int i = 0; i < Locations.Length; i++)
|
||||
{
|
||||
if (Locations[i].Type.Identifier.Equals(from, StringComparison.OrdinalIgnoreCase))
|
||||
if (Locations[i].Type.Identifier.Equals(change.CurrentType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
srcIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (srcIndex == -1) { return; }
|
||||
var upgradeLocation = Locations[srcIndex];
|
||||
upgradeLocation.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals(to, StringComparison.OrdinalIgnoreCase)));
|
||||
var location = Locations[srcIndex];
|
||||
|
||||
if (change.RequiredDurationRange.X > 0)
|
||||
{
|
||||
location.PendingLocationTypeChange = (change, Rand.Range(change.RequiredDurationRange.X, change.RequiredDurationRange.Y), Prefab);
|
||||
}
|
||||
else
|
||||
{
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals(change.ChangeToType, StringComparison.OrdinalIgnoreCase)));
|
||||
location.LocationTypeChangeCooldown = change.CooldownAfterChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,10 @@ namespace Barotrauma
|
||||
Nest = 0x10,
|
||||
Mineral = 0x20,
|
||||
Combat = 0x40,
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | Combat
|
||||
OutpostDestroy = 0x80,
|
||||
OutpostRescue = 0x100,
|
||||
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | Combat | OutpostDestroy | OutpostRescue
|
||||
}
|
||||
|
||||
partial class MissionPrefab
|
||||
@@ -33,6 +36,8 @@ namespace Barotrauma
|
||||
{ MissionType.Beacon, typeof(BeaconMission) },
|
||||
{ MissionType.Nest, typeof(NestMission) },
|
||||
{ MissionType.Mineral, typeof(MineralMission) },
|
||||
{ MissionType.OutpostDestroy, typeof(OutpostDestroyMission) },
|
||||
{ MissionType.OutpostRescue, typeof(AbandonedOutpostMission) },
|
||||
};
|
||||
public static readonly Dictionary<MissionType, Type> PvPMissionClasses = new Dictionary<MissionType, Type>()
|
||||
{
|
||||
@@ -67,14 +72,34 @@ namespace Barotrauma
|
||||
public readonly List<Tuple<string, object, SetDataAction.OperationType>> DataRewards = new List<Tuple<string, object, SetDataAction.OperationType>>();
|
||||
|
||||
public readonly int Commonness;
|
||||
public readonly int? Difficulty;
|
||||
public const int MinDifficulty = 1, MaxDifficulty = 4;
|
||||
|
||||
public readonly int Reward;
|
||||
|
||||
public readonly List<string> Headers;
|
||||
public readonly List<string> Messages;
|
||||
|
||||
//the mission can only be received when travelling from Pair.First to Pair.Second
|
||||
public readonly List<Pair<string, string>> AllowedLocationTypes;
|
||||
public readonly bool AllowRetry;
|
||||
|
||||
public readonly bool IsSideObjective;
|
||||
|
||||
/// <summary>
|
||||
/// The mission can only be received when travelling from Pair.First to Pair.Second
|
||||
/// </summary>
|
||||
public readonly List<Pair<string, string>> AllowedConnectionTypes;
|
||||
|
||||
/// <summary>
|
||||
/// The mission can only be received in these location types
|
||||
/// </summary>
|
||||
public readonly List<string> AllowedLocationTypes = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Show entities belonging to these sub categories when the mission starts
|
||||
/// </summary>
|
||||
public readonly List<string> UnhideEntitySubCategories = new List<string>();
|
||||
|
||||
public LocationTypeChange LocationTypeChangeOnCompleted;
|
||||
|
||||
public readonly XElement ConfigElement;
|
||||
|
||||
@@ -130,8 +155,14 @@ namespace Barotrauma
|
||||
Name = TextManager.Get("MissionName." + TextIdentifier, true) ?? element.GetAttributeString("name", "");
|
||||
Description = TextManager.Get("MissionDescription." + TextIdentifier, true) ?? element.GetAttributeString("description", "");
|
||||
Reward = element.GetAttributeInt("reward", 1);
|
||||
|
||||
AllowRetry = element.GetAttributeBool("allowretry", false);
|
||||
IsSideObjective = element.GetAttributeBool("sideobjective", false);
|
||||
Commonness = element.GetAttributeInt("commonness", 1);
|
||||
if (element.GetAttribute("difficulty") != null)
|
||||
{
|
||||
int difficulty = element.GetAttributeInt("difficulty", MinDifficulty);
|
||||
Difficulty = Math.Clamp(difficulty, MinDifficulty, MaxDifficulty);
|
||||
}
|
||||
|
||||
SuccessMessage = TextManager.Get("MissionSuccess." + TextIdentifier, true) ?? element.GetAttributeString("successmessage", "Mission completed successfully");
|
||||
FailureMessage = TextManager.Get("MissionFailure." + TextIdentifier, true) ?? "";
|
||||
@@ -144,7 +175,10 @@ namespace Barotrauma
|
||||
FailureMessage = element.GetAttributeString("failuremessage", "");
|
||||
}
|
||||
|
||||
SonarLabel = TextManager.Get("MissionSonarLabel." + TextIdentifier, true) ?? element.GetAttributeString("sonarlabel", "");
|
||||
SonarLabel =
|
||||
TextManager.Get("MissionSonarLabel." + TextIdentifier, true) ??
|
||||
TextManager.Get("MissionSonarLabel." + element.GetAttributeString("sonarlabel", ""), true) ??
|
||||
element.GetAttributeString("sonarlabel", "");
|
||||
SonarIconIdentifier = element.GetAttributeString("sonaricon", "");
|
||||
|
||||
MultiplayerOnly = element.GetAttributeBool("multiplayeronly", false);
|
||||
@@ -152,9 +186,11 @@ namespace Barotrauma
|
||||
|
||||
AchievementIdentifier = element.GetAttributeString("achievementidentifier", "");
|
||||
|
||||
UnhideEntitySubCategories = element.GetAttributeStringArray("unhideentitysubcategories", new string[0]).ToList();
|
||||
|
||||
Headers = new List<string>();
|
||||
Messages = new List<string>();
|
||||
AllowedLocationTypes = new List<Pair<string, string>>();
|
||||
AllowedConnectionTypes = new List<Pair<string, string>>();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
@@ -183,9 +219,20 @@ namespace Barotrauma
|
||||
messageIndex++;
|
||||
break;
|
||||
case "locationtype":
|
||||
AllowedLocationTypes.Add(new Pair<string, string>(
|
||||
subElement.GetAttributeString("from", ""),
|
||||
subElement.GetAttributeString("to", "")));
|
||||
case "connectiontype":
|
||||
if (subElement.Attribute("identifier") != null)
|
||||
{
|
||||
AllowedLocationTypes.Add(subElement.GetAttributeString("identifier", ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
AllowedConnectionTypes.Add(new Pair<string, string>(
|
||||
subElement.GetAttributeString("from", ""),
|
||||
subElement.GetAttributeString("to", "")));
|
||||
}
|
||||
break;
|
||||
case "locationtypechange":
|
||||
LocationTypeChangeOnCompleted = new LocationTypeChange(subElement.GetAttributeString("from", ""), subElement, requireChangeMessages: false, defaultProbability: 1.0f);
|
||||
break;
|
||||
case "reputation":
|
||||
case "reputationreward":
|
||||
@@ -257,19 +304,32 @@ namespace Barotrauma
|
||||
|
||||
public bool IsAllowed(Location from, Location to)
|
||||
{
|
||||
foreach (Pair<string, string> allowedLocationType in AllowedLocationTypes)
|
||||
if (from == to)
|
||||
{
|
||||
if (allowedLocationType.First.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.First.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
return
|
||||
AllowedLocationTypes.Any(lt => lt.Equals("any", StringComparison.OrdinalIgnoreCase)) ||
|
||||
AllowedLocationTypes.Any(lt => lt.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
foreach (Pair<string, string> allowedConnectionType in AllowedConnectionTypes)
|
||||
{
|
||||
if (allowedConnectionType.First.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedConnectionType.First.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (allowedLocationType.Second.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.Second.Equals(to.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
if (allowedConnectionType.Second.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedConnectionType.Second.Equals(to.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Type == MissionType.Beacon)
|
||||
{
|
||||
var connection = from.Connections.Find(c => c.Locations.Contains(from) && c.Locations.Contains(to));
|
||||
if (connection?.LevelData == null || !connection.LevelData.HasBeaconStation || connection.LevelData.IsBeaconActive) { return false; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
partial class MonsterMission : Mission
|
||||
{
|
||||
//string = filename, point = min,max
|
||||
private readonly HashSet<Tuple<CharacterPrefab, Point>> monsterPrefabs = new HashSet<Tuple<CharacterPrefab, Point>>();
|
||||
private readonly HashSet<(CharacterPrefab character, Point amountRange)> monsterPrefabs = new HashSet<(CharacterPrefab character, Point amountRange)>();
|
||||
private readonly List<Character> monsters = new List<Character>();
|
||||
private readonly List<Vector2> sonarPositions = new List<Vector2>();
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly float maxSonarMarkerDistance = 10000.0f;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace Barotrauma
|
||||
if (characterPrefab != null)
|
||||
{
|
||||
int monsterCount = Math.Min(prefab.ConfigElement.GetAttributeInt("monstercount", 1), 255);
|
||||
monsterPrefabs.Add(new Tuple<CharacterPrefab, Point>(characterPrefab, new Point(monsterCount)));
|
||||
monsterPrefabs.Add((characterPrefab, new Point(monsterCount)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -52,6 +53,13 @@ namespace Barotrauma
|
||||
|
||||
maxSonarMarkerDistance = prefab.ConfigElement.GetAttributeFloat("maxsonarmarkerdistance", 10000.0f);
|
||||
|
||||
var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||
if (string.IsNullOrWhiteSpace(spawnPosTypeStr) ||
|
||||
!Enum.TryParse(spawnPosTypeStr, true, out spawnPosType))
|
||||
{
|
||||
spawnPosType = Level.PositionType.MainPath | Level.PositionType.SidePath;
|
||||
}
|
||||
|
||||
foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster"))
|
||||
{
|
||||
speciesName = monsterElement.GetAttributeString("character", string.Empty);
|
||||
@@ -65,7 +73,7 @@ namespace Barotrauma
|
||||
var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName);
|
||||
if (characterPrefab != null)
|
||||
{
|
||||
monsterPrefabs.Add(new Tuple<CharacterPrefab, Point>(characterPrefab, new Point(min, max)));
|
||||
monsterPrefabs.Add((characterPrefab, new Point(min, max)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -75,14 +83,14 @@ namespace Barotrauma
|
||||
|
||||
if (monsterPrefabs.Any())
|
||||
{
|
||||
var characterParams = new CharacterParams(monsterPrefabs.First().Item1.FilePath);
|
||||
var characterParams = new CharacterParams(monsterPrefabs.First().character.FilePath);
|
||||
description = description.Replace("[monster]",
|
||||
TextManager.Get("character." + characterParams.SpeciesTranslationOverride, returnNull: true) ??
|
||||
TextManager.Get("character." + characterParams.SpeciesName));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (monsters.Count > 0)
|
||||
{
|
||||
@@ -106,13 +114,13 @@ namespace Barotrauma
|
||||
|
||||
if (!IsClient)
|
||||
{
|
||||
Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath | Level.PositionType.SidePath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
foreach (var monster in monsterPrefabs)
|
||||
Level.Loaded.TryGetInterestingPosition(true, spawnPosType, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
foreach (var (character, amountRange) in monsterPrefabs)
|
||||
{
|
||||
int amount = Rand.Range(monster.Item2.X, monster.Item2.Y + 1);
|
||||
int amount = Rand.Range(amountRange.X, amountRange.Y + 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
monsters.Add(Character.Create(monster.Item1.Identifier, spawnPos, ToolBox.RandomSeed(8), createNetworkEvent: false));
|
||||
monsters.Add(Character.Create(character.Identifier, spawnPos, ToolBox.RandomSeed(8), createNetworkEvent: false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,9 +221,17 @@ namespace Barotrauma
|
||||
tempSonarPositions.Clear();
|
||||
monsters.Clear();
|
||||
if (State < 1) { return; }
|
||||
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (level?.LevelData != null && Prefab.Tags.Any(t => t.Equals("huntinggrounds", StringComparison.OrdinalIgnoreCase) || t.Equals("huntinggroundsnoreward", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
level.LevelData.HasHuntingGrounds = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEliminated(Character enemy) =>
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Barotrauma
|
||||
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (items.Any())
|
||||
{
|
||||
@@ -270,7 +270,7 @@ namespace Barotrauma
|
||||
|
||||
break;
|
||||
case 1:
|
||||
if (!Submarine.MainSub.AtEndPosition && !Submarine.MainSub.AtStartPosition) { return; }
|
||||
if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit) { return; }
|
||||
State = 2;
|
||||
break;
|
||||
}
|
||||
@@ -309,7 +309,10 @@ namespace Barotrauma
|
||||
completed = true;
|
||||
if (completed)
|
||||
{
|
||||
ChangeLocationType("None", "Explored");
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Item item in items)
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class OutpostDestroyMission : AbandonedOutpostMission
|
||||
{
|
||||
private readonly string itemTag;
|
||||
private readonly XElement itemConfig;
|
||||
private readonly List<Item> items = new List<Item>();
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (State > 0)
|
||||
{
|
||||
return Enumerable.Empty<Vector2>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Targets.Select(t => t.WorldPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Entity> Targets
|
||||
{
|
||||
get
|
||||
{
|
||||
if (State > 0)
|
||||
{
|
||||
return Enumerable.Empty<Entity>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (items.Any())
|
||||
{
|
||||
return items.Where(it => !it.Removed && it.Condition > 0.0f).Cast<Entity>().Concat(requireKill.Where(c => !c.Removed && !c.IsDead)).Concat(requireRescue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return requireKill.Concat(requireRescue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OutpostDestroyMission(MissionPrefab prefab, Location[] locations) :
|
||||
base(prefab, locations)
|
||||
{
|
||||
itemConfig = prefab.ConfigElement.Element("Items");
|
||||
itemTag = prefab.ConfigElement.GetAttributeString("targetitem", "");
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
items.Clear();
|
||||
#if SERVER
|
||||
spawnedItems.Clear();
|
||||
#endif
|
||||
if (!string.IsNullOrEmpty(itemTag))
|
||||
{
|
||||
var itemsToDestroy = Item.ItemList.FindAll(it => it.Submarine?.Info.Type != SubmarineType.Player && it.HasTag(itemTag));
|
||||
if (!itemsToDestroy.Any())
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in mission \"{Prefab.Identifier}\". Could not find an item with the tag \"{itemTag}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
items.AddRange(itemsToDestroy);
|
||||
}
|
||||
}
|
||||
if (itemConfig != null && !IsClient)
|
||||
{
|
||||
foreach (XElement element in itemConfig.Elements())
|
||||
{
|
||||
string itemIdentifier = element.GetAttributeString("identifier", "");
|
||||
if (!(MapEntityPrefab.Find(null, itemIdentifier) is ItemPrefab itemPrefab))
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn item for outpost destroy mission: item prefab \"" + itemIdentifier + "\" not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] moduleFlags = element.GetAttributeStringArray("moduleflags", null);
|
||||
string[] spawnPointTags = element.GetAttributeStringArray("spawnpointtags", null);
|
||||
ISpatialEntity spawnPoint = SpawnAction.GetSpawnPos(
|
||||
SpawnAction.SpawnLocationType.Outpost, SpawnType.Human | SpawnType.Enemy,
|
||||
moduleFlags, spawnPointTags, element.GetAttributeBool("asfaraspossible", false));
|
||||
if (spawnPoint == null)
|
||||
{
|
||||
var submarine = Submarine.Loaded.Find(s => s.Info.Type == SubmarineType.Outpost) ?? Submarine.MainSub;
|
||||
spawnPoint = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandom();
|
||||
}
|
||||
Vector2 spawnPos = spawnPoint.WorldPosition;
|
||||
if (spawnPoint is WayPoint wp && wp.CurrentHull != null)
|
||||
{
|
||||
spawnPos = new Vector2(
|
||||
MathHelper.Clamp(wp.WorldPosition.X + Rand.Range(-200, 200), wp.CurrentHull.WorldRect.X, wp.CurrentHull.WorldRect.Right),
|
||||
wp.CurrentHull.WorldRect.Y - wp.CurrentHull.Rect.Height + 16.0f);
|
||||
}
|
||||
var item = new Item(itemPrefab, spawnPos, null);
|
||||
items.Add(item);
|
||||
#if SERVER
|
||||
spawnedItems.Add(item);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
base.StartMissionSpecific(level);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (requireRescue.Any(r => r.Removed || r.IsDead))
|
||||
{
|
||||
#if SERVER
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode) && GameMain.Server != null)
|
||||
{
|
||||
GameMain.Server.EndGame();
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
if (items.Any())
|
||||
{
|
||||
if (items.All(it => it.Removed || it.Condition <= 0.0f) &&
|
||||
requireKill.All(c => c.Removed || c.IsDead) &&
|
||||
requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player))
|
||||
{
|
||||
State = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (requireKill.All(c => c.Removed || c.IsDead) &&
|
||||
requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player))
|
||||
{
|
||||
State = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#if SERVER
|
||||
case 1:
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode) && GameMain.Server != null)
|
||||
{
|
||||
if (!Submarine.MainSub.AtStartExit || (wasDocked && !Submarine.MainSub.DockedTo.Contains(Level.Loaded.StartOutpost)))
|
||||
{
|
||||
GameMain.Server.EndGame();
|
||||
State = 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
#if SERVER
|
||||
originalInventoryID = Entity.NullEntityID;
|
||||
@@ -239,7 +239,7 @@ namespace Barotrauma
|
||||
State = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (!Submarine.MainSub.AtEndPosition && !Submarine.MainSub.AtStartPosition) { return; }
|
||||
if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit) { return; }
|
||||
State = 2;
|
||||
break;
|
||||
}
|
||||
@@ -248,11 +248,16 @@ namespace Barotrauma
|
||||
public override void End()
|
||||
{
|
||||
var root = item.GetRootContainer() ?? item;
|
||||
if (root.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndPosition && !root.CurrentHull.Submarine.AtStartPosition) || item.Removed)
|
||||
if (root.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
|
||||
item?.Remove();
|
||||
item = null;
|
||||
GiveReward();
|
||||
|
||||
@@ -16,16 +16,16 @@ namespace Barotrauma
|
||||
private readonly float scatter;
|
||||
private readonly float offset;
|
||||
|
||||
private readonly bool spawnDeep;
|
||||
|
||||
private Vector2? spawnPos;
|
||||
|
||||
private readonly bool disallowed;
|
||||
private bool disallowed;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
|
||||
private bool spawnPending;
|
||||
|
||||
private int maxAmountPerLevel = int.MaxValue;
|
||||
|
||||
public List<Character> Monsters => monsters;
|
||||
public Vector2? SpawnPos => spawnPos;
|
||||
public bool SpawnPending => spawnPending;
|
||||
@@ -72,15 +72,21 @@ namespace Barotrauma
|
||||
minAmount = prefab.ConfigElement.GetAttributeInt("minamount", defaultAmount);
|
||||
maxAmount = Math.Max(prefab.ConfigElement.GetAttributeInt("maxamount", 1), minAmount);
|
||||
|
||||
var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||
maxAmountPerLevel = prefab.ConfigElement.GetAttributeInt("maxamountperlevel", int.MaxValue);
|
||||
|
||||
var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||
if (string.IsNullOrWhiteSpace(spawnPosTypeStr) ||
|
||||
!Enum.TryParse(spawnPosTypeStr, true, out spawnPosType))
|
||||
{
|
||||
spawnPosType = Level.PositionType.MainPath;
|
||||
}
|
||||
|
||||
spawnDeep = prefab.ConfigElement.GetAttributeBool("spawndeep", false);
|
||||
//backwards compatibility
|
||||
if (prefab.ConfigElement.GetAttributeBool("spawndeep", false))
|
||||
{
|
||||
spawnPosType = Level.PositionType.Abyss;
|
||||
}
|
||||
|
||||
offset = prefab.ConfigElement.GetAttributeFloat("offset", 0);
|
||||
scatter = Math.Clamp(prefab.ConfigElement.GetAttributeFloat("scatter", 1000), 0, 3000);
|
||||
|
||||
@@ -163,15 +169,6 @@ namespace Barotrauma
|
||||
{
|
||||
removals.Add(position);
|
||||
}
|
||||
if (spawnDeep)
|
||||
{
|
||||
for (int i = 0; i < availablePositions.Count; i++)
|
||||
{
|
||||
var pos = availablePositions[i].Position;
|
||||
pos = new Point(pos.X, pos.Y - Level.Loaded.Size.Y);
|
||||
availablePositions[i] = new Level.InterestingPosition(pos, availablePositions[i].PositionType);
|
||||
}
|
||||
}
|
||||
if (position.Position.Y < Level.Loaded.GetBottomPosition(position.Position.X).Y)
|
||||
{
|
||||
removals.Add(position);
|
||||
@@ -196,7 +193,7 @@ namespace Barotrauma
|
||||
var availablePositions = GetAvailableSpawnPositions();
|
||||
var chosenPosition = new Level.InterestingPosition(Point.Zero, Level.PositionType.MainPath, isValid: false);
|
||||
bool isSubOrWreck = spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Wreck;
|
||||
if (affectSubImmediately && !isSubOrWreck)
|
||||
if (affectSubImmediately && !isSubOrWreck && spawnPosType != Level.PositionType.Abyss)
|
||||
{
|
||||
if (availablePositions.None())
|
||||
{
|
||||
@@ -218,7 +215,7 @@ namespace Barotrauma
|
||||
float dist = Vector2.DistanceSquared(pos, refSub.WorldPosition);
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (sub.Info.Type != SubmarineType.Player && sub != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle) { continue; }
|
||||
|
||||
float minDistToSub = GetMinDistanceToSub(sub);
|
||||
if (dist < minDistToSub * minDistToSub) { continue; }
|
||||
@@ -276,6 +273,7 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 1; i < Submarine.MainSubs.Length; i++)
|
||||
{
|
||||
if (Submarine.MainSubs[i] == null) { continue; }
|
||||
availablePositions.RemoveAll(p => Vector2.DistanceSquared(Submarine.MainSubs[i].WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
|
||||
}
|
||||
}
|
||||
@@ -301,6 +299,13 @@ namespace Barotrauma
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.ParentRuin == chosenPosition.Ruin);
|
||||
spawnPos = spawnPoint.WorldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
//no suitable position found, disable the event
|
||||
spawnPos = null;
|
||||
Finished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((chosenPosition.PositionType == Level.PositionType.MainPath || chosenPosition.PositionType == Level.PositionType.SidePath)
|
||||
&& offset > 0)
|
||||
@@ -351,6 +356,15 @@ namespace Barotrauma
|
||||
|
||||
if (spawnPos == null)
|
||||
{
|
||||
if (maxAmountPerLevel < int.MaxValue)
|
||||
{
|
||||
if (Character.CharacterList.Count(c => c.SpeciesName == speciesName) >= maxAmountPerLevel)
|
||||
{
|
||||
disallowed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FindSpawnPosition(affectSubImmediately: true);
|
||||
//the event gets marked as finished if a spawn point is not found
|
||||
if (isFinished) { return; }
|
||||
@@ -361,7 +375,7 @@ namespace Barotrauma
|
||||
if (spawnPending)
|
||||
{
|
||||
//wait until there are no submarines at the spawnpos
|
||||
if (spawnPosType == Level.PositionType.MainPath)
|
||||
if (spawnPosType == Level.PositionType.MainPath || spawnPosType == Level.PositionType.SidePath || spawnPosType == Level.PositionType.Abyss)
|
||||
{
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
@@ -400,6 +414,19 @@ namespace Barotrauma
|
||||
if (!someoneNearby) { return; }
|
||||
}
|
||||
|
||||
|
||||
if (spawnPosType == Level.PositionType.Abyss || spawnPosType == Level.PositionType.AbyssCave)
|
||||
{
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
if (submarine.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (submarine.WorldPosition.Y > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spawnPending = false;
|
||||
|
||||
//+1 because Range returns an integer less than the max value
|
||||
@@ -431,7 +458,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
monsters.Add(Character.Create(speciesName, pos, seed, characterInfo: null, isRemotePlayer: false, hasAi: true, createNetworkEvent: true));
|
||||
Character createdCharacter = Character.Create(speciesName, pos, seed, characterInfo: null, isRemotePlayer: false, hasAi: true, createNetworkEvent: true);
|
||||
if (GameMain.GameSession.IsCurrentLocationRadiated())
|
||||
{
|
||||
AfflictionPrefab radiationPrefab = AfflictionPrefab.RadiationSickness;
|
||||
Affliction affliction = new Affliction(radiationPrefab, radiationPrefab.MaxStrength);
|
||||
createdCharacter?.CharacterHealth.ApplyAffliction(null, affliction);
|
||||
// TODO test multiplayer
|
||||
createdCharacter?.Kill(CauseOfDeathType.Affliction, affliction, log: false);
|
||||
}
|
||||
monsters.Add(createdCharacter);
|
||||
|
||||
if (monsters.Count == amount)
|
||||
{
|
||||
@@ -440,7 +476,7 @@ namespace Barotrauma
|
||||
//otherwise it'll make the spawned characters act as a swarm
|
||||
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
}
|
||||
}, Rand.Range(0f, amount / 2));
|
||||
}, Rand.Range(0f, amount / 2f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace Barotrauma
|
||||
private int prevEntityCount;
|
||||
private int prevPlayerCount, prevBotCount;
|
||||
|
||||
private string[] requiredDestinationTypes;
|
||||
private readonly string[] requiredDestinationTypes;
|
||||
public readonly bool RequireBeaconStation;
|
||||
|
||||
public int CurrentActionIndex { get; private set; }
|
||||
public List<EventAction> Actions { get; } = new List<EventAction>();
|
||||
@@ -21,7 +22,7 @@ namespace Barotrauma
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "ScriptedEvent (" + prefab.EventType.ToString() +")";
|
||||
return $"ScriptedEvent ({prefab.Identifier})";
|
||||
}
|
||||
|
||||
public ScriptedEvent(EventPrefab prefab) : base(prefab)
|
||||
@@ -43,6 +44,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
requiredDestinationTypes = prefab.ConfigElement.GetAttributeStringArray("requireddestinationtypes", null);
|
||||
RequireBeaconStation = prefab.ConfigElement.GetAttributeBool("requirebeaconstation", false);
|
||||
}
|
||||
|
||||
public void AddTarget(string tag, Entity target)
|
||||
@@ -208,9 +210,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (requiredDestinationTypes == null) { return true; }
|
||||
var currLocation = GameMain.GameSession?.Campaign?.Map.CurrentLocation;
|
||||
if (currLocation == null) { return true; }
|
||||
var locations = currLocation?.Connections?.Select(c => c.Locations.First(l => l != currLocation));
|
||||
return locations.Any(l => requiredDestinationTypes.Any(t => l.Type.Identifier.Equals(t, StringComparison.OrdinalIgnoreCase)));
|
||||
if (currLocation?.Connections == null) { return true; }
|
||||
foreach (LocationConnection c in currLocation.Connections)
|
||||
{
|
||||
if (RequireBeaconStation && !c.LevelData.HasBeaconStation) { continue; }
|
||||
if (requiredDestinationTypes.Any(t => c.OtherLocation(currLocation).Type.Identifier.Equals(t, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user