Unstable v0.1300.0.0 (February 19th 2021)
This commit is contained in:
@@ -59,7 +59,7 @@ namespace Barotrauma
|
||||
(Rand.Value(Rand.RandSync.Server) < 0.5f) ?
|
||||
Level.PositionType.MainPath | Level.PositionType.SidePath :
|
||||
Level.PositionType.Cave | Level.PositionType.Ruin,
|
||||
500.0f, 10000.0f, 30.0f);
|
||||
500.0f, 10000.0f, 30.0f, SpawnPosFilter);
|
||||
|
||||
spawnPending = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -11,6 +12,8 @@ namespace Barotrauma
|
||||
|
||||
public EventPrefab Prefab => prefab;
|
||||
|
||||
public Func<Level.InterestingPosition, bool> SpawnPosFilter;
|
||||
|
||||
public bool IsFinished
|
||||
{
|
||||
get { return isFinished; }
|
||||
@@ -56,5 +59,10 @@ namespace Barotrauma
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool LevelMeetsRequirements()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class CheckAfflictionAction : BinaryOptionAction
|
||||
{
|
||||
[Serialize("", true)]
|
||||
public string Identifier { get; set; } = "";
|
||||
|
||||
[Serialize("", true)]
|
||||
public string TargetTag { get; set; } = "";
|
||||
|
||||
[Serialize(LimbType.None, true, "Only check afflictions on the specified limb type")]
|
||||
public LimbType TargetLimb { get; set; }
|
||||
|
||||
[Serialize(true, true, "When set to false when TargetLimb is not specified prevent checking limb-specific afflictions")]
|
||||
public bool AllowLimbAfflictions { get; set; }
|
||||
|
||||
public CheckAfflictionAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
|
||||
|
||||
protected override bool? DetermineSuccess()
|
||||
{
|
||||
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)
|
||||
{
|
||||
Affliction? affliction = target.CharacterHealth?.GetAffliction(Identifier, AllowLimbAfflictions);
|
||||
return affliction != null;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(HasBeenDetermined())} {nameof(CheckAfflictionAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
|
||||
$"AfflictionIdentifier: {Identifier.ColorizeObject()}, " +
|
||||
$"TargetLimb: {TargetLimb.ColorizeObject()}, " +
|
||||
$"Succeeded: {succeeded.ColorizeObject()})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -11,6 +12,12 @@ namespace Barotrauma
|
||||
[Serialize("", true)]
|
||||
public string Condition { get; set; } = null!;
|
||||
|
||||
[Serialize(false, true, "Forces the comparison to use string instead of attempting to parse it as a boolean or a float first")]
|
||||
public bool ForceString { get; set; }
|
||||
|
||||
[Serialize(false, true, "Performs the comparison against a metadata by identifier instead of a constant value")]
|
||||
public bool CheckAgainstMetadata { get; set; }
|
||||
|
||||
protected object? value2;
|
||||
protected object? value1;
|
||||
|
||||
@@ -41,13 +48,52 @@ namespace Barotrauma
|
||||
Operator = PropertyConditional.GetOperatorType(op);
|
||||
if (Operator == PropertyConditional.OperatorType.None) { return false; }
|
||||
|
||||
bool? tryBoolean = TryBoolean(campaignMode, value);
|
||||
if (tryBoolean != null) { return tryBoolean; }
|
||||
if (CheckAgainstMetadata)
|
||||
{
|
||||
object? metadata1 = campaignMode.CampaignMetadata.GetValue(Identifier);
|
||||
object? metadata2 = campaignMode.CampaignMetadata.GetValue(value);
|
||||
|
||||
bool? tryFloat = TryFloat(campaignMode, value);
|
||||
if (tryFloat != null) { return tryFloat; }
|
||||
if (metadata1 == null || metadata2 == null)
|
||||
{
|
||||
return Operator switch
|
||||
{
|
||||
PropertyConditional.OperatorType.Equals => metadata1 == metadata2,
|
||||
PropertyConditional.OperatorType.NotEquals => metadata1 != metadata2,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
if (!ForceString)
|
||||
{
|
||||
switch (metadata1)
|
||||
{
|
||||
case bool bool1 when metadata2 is bool bool2:
|
||||
return CompareBool(bool1, bool2) ?? false;
|
||||
case float float1 when metadata2 is float float2:
|
||||
return CompareFloat(float1, float2) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata1 is string string1 && metadata2 is string string2)
|
||||
{
|
||||
return CompareString(string1, string2) ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ForceString)
|
||||
{
|
||||
bool? tryBoolean = TryBoolean(campaignMode, value);
|
||||
if (tryBoolean != null) { return tryBoolean; }
|
||||
|
||||
bool? tryFloat = TryFloat(campaignMode, value);
|
||||
if (tryFloat != null) { return tryFloat; }
|
||||
}
|
||||
|
||||
bool? tryString = TryString(campaignMode, value);
|
||||
if (tryString != null) { return tryString; }
|
||||
|
||||
DebugConsole.ThrowError($"{value2} ({Condition}) did not match a boolean or a float.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,53 +101,85 @@ namespace Barotrauma
|
||||
{
|
||||
if (bool.TryParse(value, out bool b))
|
||||
{
|
||||
bool target = GetBool(campaignMode);
|
||||
value1 = target;
|
||||
value2 = b;
|
||||
switch (Operator)
|
||||
{
|
||||
case PropertyConditional.OperatorType.Equals:
|
||||
return target == b;
|
||||
case PropertyConditional.OperatorType.NotEquals:
|
||||
return target != b;
|
||||
default:
|
||||
DebugConsole.Log($"Only \"Equals\" and \"Not equals\" operators are allowed for a boolean (was {Operator} for {value}).");
|
||||
return false;
|
||||
}
|
||||
return CompareBool(GetBool(campaignMode), b);
|
||||
}
|
||||
|
||||
DebugConsole.Log($"{value} != bool");
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool? CompareBool(bool val1, bool val2)
|
||||
{
|
||||
value1 = val1;
|
||||
value2 = val2;
|
||||
switch (Operator)
|
||||
{
|
||||
case PropertyConditional.OperatorType.Equals:
|
||||
return val1 == val2;
|
||||
case PropertyConditional.OperatorType.NotEquals:
|
||||
return val1 != val2;
|
||||
default:
|
||||
DebugConsole.Log($"Only \"Equals\" and \"Not equals\" operators are allowed for a boolean (was {Operator} for {val2}).");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool? TryFloat(CampaignMode campaignMode, string value)
|
||||
{
|
||||
if (float.TryParse(value, out float f))
|
||||
{
|
||||
float target = GetFloat(campaignMode);
|
||||
value1 = target;
|
||||
value2 = f;
|
||||
switch (Operator)
|
||||
{
|
||||
case PropertyConditional.OperatorType.Equals:
|
||||
return MathUtils.NearlyEqual(target, f);
|
||||
case PropertyConditional.OperatorType.GreaterThan:
|
||||
return target > f;
|
||||
case PropertyConditional.OperatorType.GreaterThanEquals:
|
||||
return target >= f;
|
||||
case PropertyConditional.OperatorType.LessThan:
|
||||
return target < f;
|
||||
case PropertyConditional.OperatorType.LessThanEquals:
|
||||
return target <= f;
|
||||
case PropertyConditional.OperatorType.NotEquals:
|
||||
return !MathUtils.NearlyEqual(target, f);
|
||||
}
|
||||
return CompareFloat(GetFloat(campaignMode), f);
|
||||
}
|
||||
|
||||
DebugConsole.Log($"{value} != float");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private bool? CompareFloat(float val1, float val2)
|
||||
{
|
||||
value1 = val1;
|
||||
value2 = val2;
|
||||
switch (Operator)
|
||||
{
|
||||
case PropertyConditional.OperatorType.Equals:
|
||||
return MathUtils.NearlyEqual(val1, val2);
|
||||
case PropertyConditional.OperatorType.GreaterThan:
|
||||
return val1 > val2;
|
||||
case PropertyConditional.OperatorType.GreaterThanEquals:
|
||||
return val1 >= val2;
|
||||
case PropertyConditional.OperatorType.LessThan:
|
||||
return val1 < val2;
|
||||
case PropertyConditional.OperatorType.LessThanEquals:
|
||||
return val1 <= val2;
|
||||
case PropertyConditional.OperatorType.NotEquals:
|
||||
return !MathUtils.NearlyEqual(val1, val2);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool? TryString(CampaignMode campaignMode, string value)
|
||||
{
|
||||
return CompareString(GetString(campaignMode), value);
|
||||
}
|
||||
|
||||
private bool? CompareString(string val1, string val2)
|
||||
{
|
||||
value1 = val1;
|
||||
value2 = val2;
|
||||
bool equals = string.Equals(val1, val2, StringComparison.OrdinalIgnoreCase);
|
||||
switch (Operator)
|
||||
{
|
||||
case PropertyConditional.OperatorType.Equals:
|
||||
return equals;
|
||||
case PropertyConditional.OperatorType.NotEquals:
|
||||
return !equals;
|
||||
default:
|
||||
DebugConsole.Log($"Only \"Equals\" and \"Not equals\" operators are allowed for a string (was {Operator} for {val2}).");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool GetBool(CampaignMode campaignMode)
|
||||
{
|
||||
return campaignMode.CampaignMetadata.GetBoolean(Identifier);
|
||||
@@ -112,6 +190,11 @@ namespace Barotrauma
|
||||
return campaignMode.CampaignMetadata.GetFloat(Identifier);
|
||||
}
|
||||
|
||||
private string GetString(CampaignMode campaignMode)
|
||||
{
|
||||
return campaignMode.CampaignMetadata.GetString(Identifier);
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
string condition = "?";
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Xml.Linq;
|
||||
using NLog.Targets;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class ClearTagAction : EventAction
|
||||
{
|
||||
[Serialize("", true)]
|
||||
public string Tag { get; set; }
|
||||
|
||||
private bool isFinished;
|
||||
|
||||
public ClearTagAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
|
||||
|
||||
public override bool IsFinished(ref string goToLabel) => isFinished;
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Tag) && ParentEvent.Targets.ContainsKey(Tag))
|
||||
{
|
||||
ParentEvent.Targets.Remove(Tag);
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(ClearTagAction)} -> (Tag: {Tag.ColorizeObject()})";
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
-21
@@ -1,3 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -40,9 +41,15 @@ namespace Barotrauma
|
||||
[Serialize(true, true)]
|
||||
public bool WaitForInteraction { get; set; }
|
||||
|
||||
[Serialize("", true, "Tag to assign to whoever invokes the conversation")]
|
||||
public string InvokerTag { get; set; }
|
||||
|
||||
[Serialize(false, true)]
|
||||
public bool FadeToBlack { get; set; }
|
||||
|
||||
[Serialize(true, true, "Should the event end if the conversations is interrupted (e.g. if the speaker dies or falls unconscious mid-conversation). Defaults to true.")]
|
||||
public bool EndEventIfInterrupted { get; set; }
|
||||
|
||||
[Serialize("", true)]
|
||||
public string EventSprite { get; set; }
|
||||
|
||||
@@ -54,7 +61,6 @@ namespace Barotrauma
|
||||
|
||||
private Character speaker;
|
||||
|
||||
private OrderInfo? prevSpeakerOrder;
|
||||
private AIObjective prevIdleObjective, prevGotoObjective;
|
||||
|
||||
public List<SubactionGroup> Options { get; private set; }
|
||||
@@ -104,19 +110,26 @@ namespace Barotrauma
|
||||
{
|
||||
#if CLIENT
|
||||
dialogBox?.Close();
|
||||
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
|
||||
{
|
||||
if (mb.UserData as string == "ConversationAction")
|
||||
{
|
||||
(mb as GUIMessageBox)?.Close();
|
||||
}
|
||||
});
|
||||
#else
|
||||
foreach (Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (c.InGame && c.Character != null) { ServerWrite(speaker, c); }
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
ResetSpeaker();
|
||||
dialogOpened = false;
|
||||
}
|
||||
|
||||
if (Interrupted == null)
|
||||
{
|
||||
goTo = "_end";
|
||||
if (EndEventIfInterrupted) { goTo = "_end"; }
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -171,16 +184,9 @@ namespace Barotrauma
|
||||
GameMain.NetworkMember.CreateEntityEvent(speaker, new object[] { NetEntityEvent.Type.AssignCampaignInteraction });
|
||||
#endif
|
||||
var humanAI = speaker.AIController as HumanAIController;
|
||||
if (humanAI != null)
|
||||
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); }
|
||||
}
|
||||
@@ -255,7 +261,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Options.Any())
|
||||
if (ShouldInterrupt())
|
||||
{
|
||||
ResetSpeaker();
|
||||
interrupt = true;
|
||||
}
|
||||
else if (Options.Any())
|
||||
{
|
||||
Options[selectedOption].Update(deltaTime);
|
||||
}
|
||||
@@ -305,16 +316,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;
|
||||
@@ -335,6 +341,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (targetCharacter != null && !string.IsNullOrWhiteSpace(InvokerTag))
|
||||
{
|
||||
ParentEvent.AddTarget(InvokerTag, targetCharacter);
|
||||
}
|
||||
|
||||
ShowDialog(speaker, targetCharacter);
|
||||
|
||||
dialogOpened = true;
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma
|
||||
var targets = ParentEvent.GetTargets(TargetTag).Where(e => e is Character).Select(e => e as Character);
|
||||
foreach (var target in targets)
|
||||
{
|
||||
target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount, target.WorldPosition + Vector2.UnitY * 150.0f);
|
||||
target.Info?.IncreaseSkillLevel(Skill?.ToLowerInvariant(), Amount, target.Position + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Should the AI ignore this item. This will prevent outpost NPCs cleaning up or otherwise using important items intended to be left for the players.")]
|
||||
public bool IgnoreByAI { get; set; }
|
||||
|
||||
private bool spawned;
|
||||
private Entity spawnedEntity;
|
||||
|
||||
@@ -106,38 +109,17 @@ namespace Barotrauma
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
Entity.Spawner.AddToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos?.WorldPosition ?? Vector2.Zero, 100.0f), onSpawn: newCharacter =>
|
||||
{
|
||||
newCharacter.TeamID = Character.TeamType.FriendlyNPC;
|
||||
newCharacter.TeamID = CharacterTeamType.FriendlyNPC;
|
||||
newCharacter.EnableDespawn = false;
|
||||
humanPrefab.GiveItems(newCharacter, newCharacter.Submarine);
|
||||
if (LootingIsStealing)
|
||||
{
|
||||
foreach (Item item in newCharacter.Inventory.Items)
|
||||
foreach (Item item in newCharacter.Inventory.AllItems)
|
||||
{
|
||||
if (item != null) { item.SpawnedInOutpost = true; }
|
||||
}
|
||||
}
|
||||
newCharacter.CharacterHealth.MaxVitality *= humanPrefab.HealthMultiplier;
|
||||
var humanAI = newCharacter.AIController as HumanAIController;
|
||||
if (humanAI != null)
|
||||
{
|
||||
var idleObjective = humanAI.ObjectiveManager.GetObjective<AIObjectiveIdle>();
|
||||
if (idleObjective != null)
|
||||
{
|
||||
idleObjective.Behavior = humanPrefab.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));
|
||||
item.SpawnedInOutpost = true;
|
||||
}
|
||||
}
|
||||
humanPrefab.InitializeCharacter(newCharacter, spawnPos);
|
||||
if (!string.IsNullOrEmpty(TargetTag) && newCharacter != null)
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newCharacter);
|
||||
@@ -197,9 +179,16 @@ namespace Barotrauma
|
||||
}
|
||||
void onSpawned(Item newItem)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(TargetTag) && newItem != null)
|
||||
if (newItem != null)
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newItem);
|
||||
if (!string.IsNullOrEmpty(TargetTag))
|
||||
{
|
||||
ParentEvent.AddTarget(TargetTag, newItem);
|
||||
}
|
||||
if (IgnoreByAI)
|
||||
{
|
||||
newItem.AddTag("ignorebyai");
|
||||
}
|
||||
}
|
||||
spawnedEntity = newItem;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ namespace Barotrauma
|
||||
[Serialize("", true)]
|
||||
public string Tag { get; set; }
|
||||
|
||||
[Serialize(true, true)]
|
||||
public bool IgnoreIncapacitatedCharacters { get; set; }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public TagAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
|
||||
@@ -27,12 +30,26 @@ namespace Barotrauma
|
||||
|
||||
private void TagPlayers()
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsPlayer);
|
||||
if (IgnoreIncapacitatedCharacters)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsPlayer && !c.IsIncapacitated);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
private void TagBots()
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot);
|
||||
if (IgnoreIncapacitatedCharacters)
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot && !c.IsIncapacitated);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(Tag, e => e is Character c && c.IsBot);
|
||||
}
|
||||
}
|
||||
|
||||
private void TagCrew()
|
||||
|
||||
@@ -24,9 +24,12 @@ namespace Barotrauma
|
||||
[Serialize(0.0f, true, description: "Range both entities must be within to activate the trigger.")]
|
||||
public float Radius { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "If true, characters who are being targeted by some enemy cannot trigger the event.")]
|
||||
[Serialize(true, true, description: "If true, characters who are being targeted by some enemy cannot trigger the action.")]
|
||||
public bool DisableInCombat { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "If true, dead/unconscious characters cannot trigger the action.")]
|
||||
public bool DisableIfTargetIncapacitated { get; set; }
|
||||
|
||||
private float distance;
|
||||
|
||||
public TriggerAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element)
|
||||
@@ -59,6 +62,7 @@ namespace Barotrauma
|
||||
foreach (Entity e1 in targets1)
|
||||
{
|
||||
if (DisableInCombat && IsInCombat(e1)) { continue; }
|
||||
if (DisableIfTargetIncapacitated && e1 is Character character1 && (character1.IsDead || character1.IsIncapacitated)) { continue; }
|
||||
if (!string.IsNullOrEmpty(TargetModuleType))
|
||||
{
|
||||
if (IsCloseEnoughToHull(e1, out Hull hull))
|
||||
@@ -75,6 +79,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (e1 == e2) { continue; }
|
||||
if (DisableInCombat && IsInCombat(e2)) { continue; }
|
||||
if (DisableIfTargetIncapacitated && e2 is Character character2 && (character2.IsDead || character2.IsIncapacitated)) { continue; }
|
||||
|
||||
Vector2 pos1 = e1.WorldPosition;
|
||||
Vector2 pos2 = e2.WorldPosition;
|
||||
|
||||
@@ -33,7 +33,11 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.GameSession.EventManager.QueuedEvents.Enqueue(eventPrefab.CreateInstance());
|
||||
var ev = eventPrefab.CreateInstance();
|
||||
if (ev != null)
|
||||
{
|
||||
GameMain.GameSession.EventManager.QueuedEvents.Enqueue(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -51,6 +52,12 @@ namespace Barotrauma
|
||||
|
||||
private float roundDuration;
|
||||
|
||||
private bool isCrewAway;
|
||||
//how long it takes after the crew returns for the event manager to resume normal operation
|
||||
const float CrewAwayResetDelay = 60.0f;
|
||||
private float crewAwayResetTimer;
|
||||
private float crewAwayDuration;
|
||||
|
||||
private readonly List<EventSet> pendingEventSets = new List<EventSet>();
|
||||
|
||||
private readonly Dictionary<EventSet, List<Event>> selectedEvents = new Dictionary<EventSet, List<Event>>();
|
||||
@@ -86,6 +93,8 @@ namespace Barotrauma
|
||||
|
||||
public void StartRound(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
|
||||
if (isClient) { return; }
|
||||
|
||||
pendingEventSets.Clear();
|
||||
@@ -100,7 +109,6 @@ namespace Barotrauma
|
||||
totalPathLength = steeringPath.TotalLength;
|
||||
}
|
||||
|
||||
this.level = level;
|
||||
SelectSettings();
|
||||
|
||||
var initialEventSet = SelectRandomEvents(EventSet.List);
|
||||
@@ -144,6 +152,9 @@ namespace Barotrauma
|
||||
PreloadContent(GetFilesToPreload());
|
||||
|
||||
roundDuration = 0.0f;
|
||||
isCrewAway = false;
|
||||
crewAwayDuration = 0.0f;
|
||||
crewAwayResetTimer = 0.0f;
|
||||
intensityUpdateTimer = 0.0f;
|
||||
CalculateCurrentIntensity(0.0f);
|
||||
currentIntensity = targetIntensity;
|
||||
@@ -258,26 +269,23 @@ namespace Barotrauma
|
||||
var doc = characterPrefab.XDocument;
|
||||
var rootElement = doc.Root;
|
||||
var mainElement = rootElement.IsOverride() ? rootElement.FirstElement() : rootElement;
|
||||
|
||||
foreach (var soundElement in mainElement.GetChildElements("sound"))
|
||||
{
|
||||
var sound = Submarine.LoadRoundSound(soundElement);
|
||||
}
|
||||
string speciesName = mainElement.GetAttributeString("speciesname", null);
|
||||
if (string.IsNullOrWhiteSpace(speciesName))
|
||||
{
|
||||
speciesName = mainElement.GetAttributeString("name", null);
|
||||
if (!string.IsNullOrWhiteSpace(speciesName))
|
||||
{
|
||||
DebugConsole.NewMessage($"Error in {file.Path}: 'name' is deprecated! Use 'speciesname' instead.", Color.Orange);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Species name null in {file.Path}");
|
||||
}
|
||||
}
|
||||
|
||||
mainElement.GetChildElements("sound").ForEach(e => Submarine.LoadRoundSound(e));
|
||||
if (!CharacterPrefab.CheckSpeciesName(mainElement, file.Path, out string speciesName)) { continue; }
|
||||
bool humanoid = mainElement.GetAttributeBool("humanoid", false);
|
||||
CharacterPrefab originalCharacter;
|
||||
if (characterPrefab.VariantOf != null)
|
||||
{
|
||||
originalCharacter = CharacterPrefab.FindBySpeciesName(characterPrefab.VariantOf);
|
||||
var originalRoot = originalCharacter.XDocument.Root;
|
||||
var originalMainElement = originalRoot.IsOverride() ? originalRoot.FirstElement() : originalRoot;
|
||||
originalMainElement.GetChildElements("sound").ForEach(e => Submarine.LoadRoundSound(e));
|
||||
if (!CharacterPrefab.CheckSpeciesName(mainElement, file.Path, out string name)) { continue; }
|
||||
speciesName = name;
|
||||
if (mainElement.Attribute("humanoid") == null)
|
||||
{
|
||||
humanoid = originalMainElement.GetAttributeBool("humanoid", false);
|
||||
}
|
||||
}
|
||||
RagdollParams ragdollParams;
|
||||
if (humanoid)
|
||||
{
|
||||
@@ -335,13 +343,31 @@ namespace Barotrauma
|
||||
{
|
||||
if (level == null) { return; }
|
||||
int applyCount = 1;
|
||||
List<Func<Level.InterestingPosition, bool>> spawnPosFilter = new List<Func<Level.InterestingPosition, bool>>();
|
||||
if (eventSet.PerRuin)
|
||||
{
|
||||
applyCount = Level.Loaded.Ruins.Count();
|
||||
foreach (var ruin in Level.Loaded.Ruins)
|
||||
{
|
||||
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Ruin == ruin; });
|
||||
}
|
||||
}
|
||||
else if (eventSet.PerCave)
|
||||
{
|
||||
applyCount = Level.Loaded.Caves.Count();
|
||||
foreach (var cave in Level.Loaded.Caves)
|
||||
{
|
||||
spawnPosFilter.Add((Level.InterestingPosition pos) => { return pos.Cave == cave; });
|
||||
}
|
||||
}
|
||||
else if (eventSet.PerWreck)
|
||||
{
|
||||
applyCount = Submarine.Loaded.Count(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; });
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
@@ -356,7 +382,9 @@ namespace Barotrauma
|
||||
if (eventPrefab != null)
|
||||
{
|
||||
var newEvent = eventPrefab.First.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
if (i < spawnPosFilter.Count) { newEvent.SpawnPosFilter = spawnPosFilter[i]; }
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
@@ -378,6 +406,7 @@ namespace Barotrauma
|
||||
foreach (Pair<EventPrefab, float> eventPrefab in eventSet.EventPrefabs)
|
||||
{
|
||||
var newEvent = eventPrefab.First.CreateInstance();
|
||||
if (newEvent == null) { continue; }
|
||||
newEvent.Init(true);
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
@@ -402,10 +431,11 @@ namespace Barotrauma
|
||||
|
||||
var allowedEventSets =
|
||||
eventSets.Where(es => level.Difficulty >= es.MinLevelDifficulty && level.Difficulty <= es.MaxLevelDifficulty && level.LevelData.Type == es.LevelType);
|
||||
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.Map?.CurrentLocation?.Type != null)
|
||||
|
||||
LocationType locationType = (GameMain.GameSession?.GameMode as CampaignMode)?.Map?.CurrentLocation?.Type ?? level?.StartLocation?.Type;
|
||||
if (locationType != 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));
|
||||
@@ -440,6 +470,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (eventSet.DelayWhenCrewAway)
|
||||
{
|
||||
if ((isCrewAway && crewAwayDuration < settings.FreezeDurationWhenCrewAway) || crewAwayResetTimer > 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) &&
|
||||
roundDuration < eventSet.MinMissionTime)
|
||||
{
|
||||
@@ -491,6 +529,25 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (IsCrewAway())
|
||||
{
|
||||
isCrewAway = true;
|
||||
crewAwayResetTimer = CrewAwayResetDelay;
|
||||
crewAwayDuration += deltaTime;
|
||||
}
|
||||
else if (crewAwayResetTimer > 0.0f)
|
||||
{
|
||||
isCrewAway = false;
|
||||
crewAwayResetTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
isCrewAway = false;
|
||||
crewAwayDuration = 0.0f;
|
||||
eventThreshold += settings.EventThresholdIncrease * deltaTime;
|
||||
eventCoolDown -= deltaTime;
|
||||
}
|
||||
|
||||
calculateDistanceTraveledTimer -= deltaTime;
|
||||
if (calculateDistanceTraveledTimer <= 0.0f)
|
||||
{
|
||||
@@ -498,9 +555,6 @@ namespace Barotrauma
|
||||
calculateDistanceTraveledTimer = CalculateDistanceTraveledInterval;
|
||||
}
|
||||
|
||||
eventThreshold += settings.EventThresholdIncrease * deltaTime;
|
||||
eventCoolDown -= deltaTime;
|
||||
|
||||
if (currentIntensity < eventThreshold)
|
||||
{
|
||||
bool recheck = false;
|
||||
@@ -524,7 +578,10 @@ namespace Barotrauma
|
||||
{
|
||||
activeEvents.Add(ev);
|
||||
eventThreshold = settings.DefaultEventThreshold;
|
||||
eventCoolDown = settings.EventCooldown;
|
||||
if (eventSet.TriggerEventCooldown && selectedEvents[eventSet].Any(e => e.Prefab.TriggerEventCooldown))
|
||||
{
|
||||
eventCoolDown = settings.EventCooldown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +618,7 @@ namespace Barotrauma
|
||||
int characterCount = 0;
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.IsDead || character.TeamID == Character.TeamType.FriendlyNPC) { continue; }
|
||||
if (character.IsDead || character.TeamID == CharacterTeamType.FriendlyNPC) { continue; }
|
||||
if (character.AIController is HumanAIController || character.IsRemotePlayer)
|
||||
{
|
||||
avgCrewHealth += character.Vitality / character.MaxVitality * (character.IsUnconscious ? 0.5f : 1.0f);
|
||||
@@ -584,9 +641,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.IsDead || character.IsIncapacitated || !character.Enabled || character.IsPet || character.Params.CompareGroup("human")) { continue; }
|
||||
|
||||
EnemyAIController enemyAI = character.AIController as EnemyAIController;
|
||||
if (enemyAI == null) continue;
|
||||
|
||||
if (!(character.AIController is EnemyAIController enemyAI)) { continue; }
|
||||
|
||||
if (character.CurrentHull?.Submarine != null &&
|
||||
(character.CurrentHull.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(character.CurrentHull.Submarine)))
|
||||
{
|
||||
@@ -679,7 +735,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds all actions in a ScriptedEvent
|
||||
/// </summary>
|
||||
@@ -748,5 +803,74 @@ namespace Barotrauma
|
||||
#endif
|
||||
return refEntity;
|
||||
}
|
||||
|
||||
private bool IsCrewAway()
|
||||
{
|
||||
#if CLIENT
|
||||
return Character.Controlled != null && IsCharacterAway(Character.Controlled);
|
||||
#else
|
||||
int playerCount = 0;
|
||||
int awayPlayerCount = 0;
|
||||
foreach (Barotrauma.Networking.Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (client.Character == null || client.Character.IsDead || client.Character.IsIncapacitated) { continue; }
|
||||
|
||||
playerCount++;
|
||||
if (IsCharacterAway(client.Character)) { awayPlayerCount++; }
|
||||
}
|
||||
return playerCount > 0 && awayPlayerCount / (float)playerCount > 0.5f;
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool IsCharacterAway(Character character)
|
||||
{
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
switch (character.Submarine.Info.Type)
|
||||
{
|
||||
case SubmarineType.Player:
|
||||
case SubmarineType.Outpost:
|
||||
case SubmarineType.OutpostModule:
|
||||
return false;
|
||||
case SubmarineType.Wreck:
|
||||
case SubmarineType.BeaconStation:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const int maxDist = 1000;
|
||||
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
foreach (var ruin in Level.Loaded.Ruins)
|
||||
{
|
||||
Rectangle area = ruin.Area;
|
||||
area.Inflate(maxDist, maxDist);
|
||||
if (area.Contains(character.WorldPosition)) { return true; }
|
||||
}
|
||||
foreach (var cave in Level.Loaded.Caves)
|
||||
{
|
||||
Rectangle area = cave.Area;
|
||||
area.Inflate(maxDist, maxDist);
|
||||
if (area.Contains(character.WorldPosition)) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub.Info.Type != SubmarineType.BeaconStation && sub.Info.Type != SubmarineType.Wreck) { continue; }
|
||||
Rectangle worldBorders = new Rectangle(
|
||||
sub.Borders.X + (int)sub.WorldPosition.X - maxDist,
|
||||
sub.Borders.Y + (int)sub.WorldPosition.Y + maxDist,
|
||||
sub.Borders.Width + maxDist * 2,
|
||||
sub.Borders.Height + maxDist * 2);
|
||||
if (Submarine.RectContains(worldBorders, character.WorldPosition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace Barotrauma
|
||||
public readonly float MinLevelDifficulty = 0.0f;
|
||||
public readonly float MaxLevelDifficulty = 100.0f;
|
||||
|
||||
public readonly float FreezeDurationWhenCrewAway = 60.0f * 10.0f;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
List.Clear();
|
||||
@@ -77,6 +79,8 @@ namespace Barotrauma
|
||||
|
||||
MinLevelDifficulty = element.GetAttributeFloat("MinLevelDifficulty", 0.0f);
|
||||
MaxLevelDifficulty = element.GetAttributeFloat("MaxLevelDifficulty", 100.0f);
|
||||
|
||||
FreezeDurationWhenCrewAway = element.GetAttributeFloat("FreezeDurationWhenCrewAway", 10.0f * 60.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ 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;
|
||||
|
||||
@@ -17,8 +17,6 @@ namespace Barotrauma
|
||||
{
|
||||
ConfigElement = element;
|
||||
|
||||
MusicType = element.GetAttributeString("musictype", "default");
|
||||
|
||||
try
|
||||
{
|
||||
EventType = Type.GetType("Barotrauma." + ConfigElement.Name, true, true);
|
||||
@@ -35,6 +33,7 @@ namespace Barotrauma
|
||||
Identifier = ConfigElement.GetAttributeString("identifier", string.Empty);
|
||||
Commonness = element.GetAttributeFloat("commonness", 1.0f);
|
||||
SpawnProbability = Math.Clamp(element.GetAttributeFloat("spawnprobability", 1.0f), 0, 1);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||
}
|
||||
|
||||
public Event CreateInstance()
|
||||
@@ -50,6 +49,9 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString());
|
||||
}
|
||||
|
||||
Event ev = (Event)instance;
|
||||
if (!ev.LevelMeetsRequirements()) { return null; }
|
||||
|
||||
return (Event)instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,11 +83,14 @@ namespace Barotrauma
|
||||
|
||||
public readonly bool IgnoreCoolDown;
|
||||
|
||||
public readonly bool PerRuin;
|
||||
public readonly bool PerWreck;
|
||||
public readonly bool PerRuin, PerCave, PerWreck;
|
||||
|
||||
public readonly bool OncePerOutpost;
|
||||
|
||||
public readonly bool DelayWhenCrewAway;
|
||||
|
||||
public readonly bool TriggerEventCooldown;
|
||||
|
||||
public readonly Dictionary<string, float> Commonness;
|
||||
|
||||
//Pair.First: event prefab, Pair.Second: commonness
|
||||
@@ -133,10 +136,13 @@ namespace Barotrauma
|
||||
MinMissionTime = element.GetAttributeFloat("minmissiontime", 0.0f);
|
||||
|
||||
AllowAtStart = element.GetAttributeBool("allowatstart", false);
|
||||
IgnoreCoolDown = element.GetAttributeBool("ignorecooldown", parentSet?.IgnoreCoolDown ?? false);
|
||||
PerRuin = element.GetAttributeBool("perruin", false);
|
||||
PerCave = element.GetAttributeBool("percave", false);
|
||||
PerWreck = element.GetAttributeBool("perwreck", false);
|
||||
IgnoreCoolDown = element.GetAttributeBool("ignorecooldown", parentSet?.IgnoreCoolDown ?? (PerRuin || PerCave || PerWreck));
|
||||
DelayWhenCrewAway = element.GetAttributeBool("delaywhencrewaway", !PerRuin && !PerCave && !PerWreck);
|
||||
OncePerOutpost = element.GetAttributeBool("perwreck", false);
|
||||
TriggerEventCooldown = element.GetAttributeBool("triggereventcooldown", true);
|
||||
|
||||
Commonness[""] = 1.0f;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AbandonedOutpostMission : Mission
|
||||
{
|
||||
private readonly XElement characterConfig;
|
||||
|
||||
private readonly List<Character> characters = new List<Character>();
|
||||
private readonly Dictionary<Character, List<Item>> characterItems = new Dictionary<Character, List<Item>>();
|
||||
|
||||
private readonly string itemTag;
|
||||
|
||||
private Item itemToDestroy;
|
||||
|
||||
public AbandonedOutpostMission(MissionPrefab prefab, Location[] locations) :
|
||||
base(prefab, locations)
|
||||
{
|
||||
characterConfig = prefab.ConfigElement.Element("Characters");
|
||||
|
||||
itemTag = prefab.ConfigElement.GetAttributeString("targetitem", "");
|
||||
if (string.IsNullOrEmpty(itemTag))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in mission prefab \"{prefab.Identifier}\". Target item not defined.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
itemToDestroy = null;
|
||||
itemToDestroy = Item.ItemList.Find(it => it.Submarine?.Info.Type != SubmarineType.Player && it.HasTag(itemTag));
|
||||
if (itemToDestroy == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in mission \"{Prefab.Identifier}\". Could not find an item with the tag \"{itemTag}\".");
|
||||
}
|
||||
|
||||
if (!IsClient)
|
||||
{
|
||||
InitCharacters();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitCharacters()
|
||||
{
|
||||
characters.Clear();
|
||||
characterItems.Clear();
|
||||
|
||||
if (characterConfig == null) { return; }
|
||||
|
||||
var submarine = Submarine.Loaded.Find(s => s.Info.Type == SubmarineType.Outpost) ?? Submarine.MainSub;
|
||||
if (submarine.Info.Type == SubmarineType.Outpost)
|
||||
{
|
||||
submarine.TeamID = CharacterTeamType.None;
|
||||
}
|
||||
|
||||
foreach (XElement element in characterConfig.Elements())
|
||||
{
|
||||
string characterIdentifier = element.GetAttributeString("identifier", "");
|
||||
string characterFrom = element.GetAttributeString("from", "");
|
||||
HumanPrefab humanPrefab = NPCSet.Get(characterFrom, characterIdentifier);
|
||||
if (humanPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn character for abandoned outpost mission: character prefab \"" + characterIdentifier + "\" not found");
|
||||
return;
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
spawnedCharacter.TeamID = CharacterTeamType.None;
|
||||
humanPrefab.InitializeCharacter(spawnedCharacter, spawnPos);
|
||||
humanPrefab.GiveItems(spawnedCharacter, Submarine.MainSub, Rand.RandSync.Server, createNetworkEvents: false);
|
||||
|
||||
characters.Add(spawnedCharacter);
|
||||
characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (State == 0 && itemToDestroy != null && itemToDestroy.Condition <= 0.0f)
|
||||
{
|
||||
State = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
completed = itemToDestroy == null || itemToDestroy.Condition <= 0.0f;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -11,11 +9,9 @@ namespace Barotrauma
|
||||
partial class BeaconMission : Mission
|
||||
{
|
||||
private bool swarmSpawned;
|
||||
private string monsterSpeciesName;
|
||||
private readonly string monsterSpeciesName;
|
||||
private Point monsterCountRange;
|
||||
private Level level;
|
||||
private Location[] locations;
|
||||
private string sonarLabel;
|
||||
private readonly string sonarLabel;
|
||||
|
||||
public BeaconMission(MissionPrefab prefab, Location[] locations) : base(prefab, locations)
|
||||
{
|
||||
@@ -34,8 +30,6 @@ namespace Barotrauma
|
||||
|
||||
monsterCountRange = new Point(min, max);
|
||||
|
||||
this.locations = locations;
|
||||
|
||||
sonarLabel = TextManager.Get("beaconstationsonarlabel");
|
||||
}
|
||||
|
||||
@@ -51,27 +45,58 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return level.BeaconStation.WorldPosition;
|
||||
if (level.BeaconStation == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
yield return level.BeaconStation.WorldPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (IsClient) { return; }
|
||||
if (!swarmSpawned && level.CheckBeaconActive())
|
||||
{
|
||||
State = 1;
|
||||
|
||||
Vector2 spawnPos = level.BeaconStation.WorldPosition;
|
||||
spawnPos.Y += level.BeaconStation.GetDockedBorders().Height * 1.5f;
|
||||
|
||||
var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p =>
|
||||
p.PositionType == Level.PositionType.MainPath ||
|
||||
p.PositionType == Level.PositionType.SidePath);
|
||||
availablePositions.RemoveAll(p => Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(p.Position.ToVector2())));
|
||||
availablePositions.RemoveAll(p => Submarine.FindContaining(p.Position.ToVector2()) != null);
|
||||
|
||||
if (availablePositions.Any())
|
||||
{
|
||||
Level.InterestingPosition? closestPos = null;
|
||||
float closestDist = float.PositiveInfinity;
|
||||
foreach (var pos in availablePositions)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(pos.Position.ToVector2(), level.BeaconStation.WorldPosition);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
closestPos = pos;
|
||||
}
|
||||
}
|
||||
if (closestPos.HasValue)
|
||||
{
|
||||
spawnPos = closestPos.Value.Position.ToVector2();
|
||||
}
|
||||
}
|
||||
|
||||
int amount = Rand.Range(monsterCountRange.X, monsterCountRange.Y + 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(monsterSpeciesName, spawnPos);
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
//round ended before the coroutine finished
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
Entity.Spawner.AddToSpawnQueue(monsterSpeciesName, spawnPos);
|
||||
}, Rand.Range(0f, amount));
|
||||
}
|
||||
swarmSpawned = true;
|
||||
}
|
||||
@@ -82,13 +107,15 @@ namespace Barotrauma
|
||||
completed = level.CheckBeaconActive();
|
||||
if (completed)
|
||||
{
|
||||
if (GameMain.GameSession.GameMode is CampaignMode)
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
int naturalFormationIndex = locations[0].Type.Identifier.Equals("None", StringComparison.OrdinalIgnoreCase) ? 0 : 1;
|
||||
var upgradeLocation = locations[naturalFormationIndex];
|
||||
upgradeLocation.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals("Explored", StringComparison.OrdinalIgnoreCase)));
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
if (level?.LevelData != null)
|
||||
{
|
||||
level.LevelData.IsBeaconActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,10 @@ namespace Barotrauma
|
||||
cargoSpawnPos.Position.X + Rand.Range(-20.0f, 20.0f, Rand.RandSync.Server),
|
||||
cargoRoom.Rect.Y - cargoRoom.Rect.Height + itemPrefab.Size.Y / 2);
|
||||
|
||||
var item = new Item(itemPrefab, position, cargoRoom.Submarine);
|
||||
var item = new Item(itemPrefab, position, cargoRoom.Submarine)
|
||||
{
|
||||
SpawnedInOutpost = true
|
||||
};
|
||||
item.FindHull();
|
||||
items.Add(item);
|
||||
|
||||
@@ -115,7 +118,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
items.Clear();
|
||||
parentInventoryIDs.Clear();
|
||||
@@ -135,6 +138,10 @@ namespace Barotrauma
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ namespace Barotrauma
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
private Character.TeamType Winner
|
||||
private CharacterTeamType Winner
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.GameSession?.WinningTeam == null) { return Character.TeamType.None; }
|
||||
if (GameMain.GameSession?.WinningTeam == null) { return CharacterTeamType.None; }
|
||||
return GameMain.GameSession.WinningTeam.Value;
|
||||
}
|
||||
}
|
||||
@@ -29,14 +29,14 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Winner == Character.TeamType.None || string.IsNullOrEmpty(base.SuccessMessage)) { return ""; }
|
||||
if (Winner == CharacterTeamType.None || string.IsNullOrEmpty(base.SuccessMessage)) { return ""; }
|
||||
|
||||
//disable success message for now if it hasn't been translated
|
||||
if (!TextManager.ContainsTag("MissionSuccess." + Prefab.TextIdentifier)) { return ""; }
|
||||
|
||||
var loser = Winner == Character.TeamType.Team1 ?
|
||||
Character.TeamType.Team2 :
|
||||
Character.TeamType.Team1;
|
||||
var loser = Winner == CharacterTeamType.Team1 ?
|
||||
CharacterTeamType.Team2 :
|
||||
CharacterTeamType.Team1;
|
||||
|
||||
return base.SuccessMessage
|
||||
.Replace("[loser]", GetTeamName(loser))
|
||||
@@ -44,11 +44,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override int TeamCount
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
|
||||
public CombatMission(MissionPrefab prefab, Location[] locations)
|
||||
: base(prefab, locations)
|
||||
{
|
||||
@@ -74,13 +69,13 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetTeamName(Character.TeamType teamID)
|
||||
public static string GetTeamName(CharacterTeamType teamID)
|
||||
{
|
||||
if (teamID == Character.TeamType.Team1)
|
||||
if (teamID == CharacterTeamType.Team1)
|
||||
{
|
||||
return teamNames.Length > 0 ? teamNames[0] : "Team 1";
|
||||
}
|
||||
else if (teamID == Character.TeamType.Team2)
|
||||
else if (teamID == CharacterTeamType.Team2)
|
||||
{
|
||||
return teamNames.Length > 1 ? teamNames[1] : "Team 2";
|
||||
}
|
||||
@@ -91,11 +86,11 @@ namespace Barotrauma
|
||||
public bool IsInWinningTeam(Character character)
|
||||
{
|
||||
return character != null &&
|
||||
Winner != Character.TeamType.None &&
|
||||
Winner != CharacterTeamType.None &&
|
||||
Winner == character.TeamID;
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (GameMain.NetworkMember == null)
|
||||
{
|
||||
@@ -104,7 +99,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
subs = new Submarine[] { Submarine.MainSubs[0], Submarine.MainSubs[1] };
|
||||
subs[0].TeamID = Character.TeamType.Team1; subs[1].TeamID = Character.TeamType.Team2;
|
||||
subs[0].TeamID = CharacterTeamType.Team1; subs[1].TeamID = CharacterTeamType.Team2;
|
||||
subs[0].NeutralizeBallast(); subs[1].NeutralizeBallast();
|
||||
subs[1].SetPosition(subs[1].FindSpawnPos(Level.Loaded.EndPosition));
|
||||
subs[1].FlipX();
|
||||
@@ -120,9 +115,9 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (GameMain.NetworkMember == null) return;
|
||||
if (GameMain.NetworkMember == null) { return; }
|
||||
|
||||
if (Winner != Character.TeamType.None)
|
||||
if (Winner != CharacterTeamType.None)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Barotrauma
|
||||
private Dictionary<string, Item[]> RelevantLevelResources { get; } = new Dictionary<string, Item[]>();
|
||||
private List<Tuple<string, Vector2>> MissionClusterPositions { get; } = new List<Tuple<string, Vector2>>();
|
||||
|
||||
private readonly HashSet<Level.Cave> caves = new HashSet<Level.Cave>();
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get
|
||||
@@ -42,17 +44,72 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (SpawnedResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"SpawnedResources.Count > 0 ({SpawnedResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Spawned resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
SpawnedResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (RelevantLevelResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"RelevantLevelResources.Count > 0 ({RelevantLevelResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Relevant level resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
RelevantLevelResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (MissionClusterPositions.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"MissionClusterPositions.Count > 0 ({MissionClusterPositions.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Mission cluster positions list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
MissionClusterPositions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
caves.Clear();
|
||||
|
||||
if (IsClient) { return; }
|
||||
foreach (var kvp in ResourceClusters)
|
||||
{
|
||||
var prefab = ItemPrefab.Find(null, kvp.Key);
|
||||
if (prefab == null) { continue; }
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"couldn't find an item prefab with the identifier " + kvp.Key);
|
||||
continue;
|
||||
}
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, kvp.Value.First, out float rotation);
|
||||
if (spawnedResources.Count < kvp.Value.First)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"spawned " + spawnedResources.Count + "/" + kvp.Value.First + " of " + prefab.Name);
|
||||
}
|
||||
if (spawnedResources.None()) { continue; }
|
||||
SpawnedResources.Add(kvp.Key, spawnedResources);
|
||||
kvp.Value.Second = rotation;
|
||||
|
||||
foreach (Level.Cave cave in Level.Loaded.Caves)
|
||||
{
|
||||
foreach (Item spawnedResource in spawnedResources)
|
||||
{
|
||||
if (cave.Area.Contains(spawnedResource.WorldPosition))
|
||||
{
|
||||
cave.DisplayOnSonar = true;
|
||||
caves.Add(cave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CalculateMissionClusterPositions();
|
||||
FindRelevantLevelResources();
|
||||
@@ -76,9 +133,29 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (!EnoughHaveBeenCollected()) { return; }
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (EnoughHaveBeenCollected())
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
foreach (var kvp in SpawnedResources)
|
||||
{
|
||||
foreach (var i in kvp.Value)
|
||||
{
|
||||
if (i != null && !i.Removed && !HasBeenCollected(i))
|
||||
{
|
||||
i.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
SpawnedResources.Clear();
|
||||
RelevantLevelResources.Clear();
|
||||
MissionClusterPositions.Clear();
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
|
||||
private void FindRelevantLevelResources()
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -11,6 +9,9 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly MissionPrefab Prefab;
|
||||
protected bool completed, failed;
|
||||
|
||||
protected Level level;
|
||||
|
||||
protected int state;
|
||||
public int State
|
||||
{
|
||||
@@ -21,7 +22,7 @@ namespace Barotrauma
|
||||
{
|
||||
state = value;
|
||||
#if SERVER
|
||||
GameMain.Server?.UpdateMissionState(state);
|
||||
GameMain.Server?.UpdateMissionState(this, state);
|
||||
#endif
|
||||
ShowMessage(State);
|
||||
}
|
||||
@@ -85,11 +86,6 @@ namespace Barotrauma
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual int TeamCount
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get { return Enumerable.Empty<Vector2>(); }
|
||||
@@ -180,15 +176,23 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void Start(Level level) { }
|
||||
public void Start(Level level)
|
||||
{
|
||||
foreach (string categoryToShow in Prefab.UnhideEntitySubCategories)
|
||||
{
|
||||
foreach (MapEntity entityToShow in MapEntity.mapEntityList.Where(me => me.prefab.HasSubCategory(categoryToShow)))
|
||||
{
|
||||
entityToShow.HiddenInGame = false;
|
||||
}
|
||||
}
|
||||
this.level = level;
|
||||
StartMissionSpecific(level);
|
||||
}
|
||||
|
||||
protected virtual void StartMissionSpecific(Level level) { }
|
||||
|
||||
public virtual void Update(float deltaTime) { }
|
||||
|
||||
public virtual void AssignTeamIDs(List<Networking.Client> clients)
|
||||
{
|
||||
clients.ForEach(c => c.TeamID = Character.TeamType.Team1);
|
||||
}
|
||||
|
||||
protected void ShowMessage(int missionState)
|
||||
{
|
||||
ShowMessageProjSpecific(missionState);
|
||||
@@ -202,7 +206,10 @@ namespace Barotrauma
|
||||
public virtual void End()
|
||||
{
|
||||
completed = true;
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
}
|
||||
|
||||
@@ -234,6 +241,35 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
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(change.CurrentType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
srcIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (srcIndex == -1) { return; }
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AdjustLevelData(LevelData levelData) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ namespace Barotrauma
|
||||
Nest = 0x10,
|
||||
Mineral = 0x20,
|
||||
Combat = 0x40,
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | Combat
|
||||
AbandonedOutpost = 0x80,
|
||||
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | AbandonedOutpost
|
||||
}
|
||||
|
||||
partial class MissionPrefab
|
||||
@@ -33,6 +35,7 @@ namespace Barotrauma
|
||||
{ MissionType.Beacon, typeof(BeaconMission) },
|
||||
{ MissionType.Nest, typeof(NestMission) },
|
||||
{ MissionType.Mineral, typeof(MineralMission) },
|
||||
{ MissionType.AbandonedOutpost, typeof(AbandonedOutpostMission) },
|
||||
};
|
||||
public static readonly Dictionary<MissionType, Type> PvPMissionClasses = new Dictionary<MissionType, Type>()
|
||||
{
|
||||
@@ -73,8 +76,26 @@ namespace Barotrauma
|
||||
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,7 +151,8 @@ 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);
|
||||
|
||||
SuccessMessage = TextManager.Get("MissionSuccess." + TextIdentifier, true) ?? element.GetAttributeString("successmessage", "Mission completed successfully");
|
||||
@@ -152,9 +174,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 +207,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 +292,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly float maxSonarMarkerDistance = 10000.0f;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
@@ -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);
|
||||
@@ -81,22 +89,32 @@ namespace Barotrauma
|
||||
TextManager.Get("character." + characterParams.SpeciesName));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (monsters.Count > 0)
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"monsters.Count > 0 ({monsters.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Monster list was not empty at the start of a monster mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
monsters.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (tempSonarPositions.Count > 0)
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"tempSonarPositions.Count > 0 ({tempSonarPositions.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Sonar position list was not empty at the start of a monster mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
tempSonarPositions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!IsClient)
|
||||
{
|
||||
Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath | Level.PositionType.SidePath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
Level.Loaded.TryGetInterestingPosition(true, spawnPosType, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
foreach (var monster in monsterPrefabs)
|
||||
{
|
||||
int amount = Rand.Range(monster.Item2.X, monster.Item2.Y + 1);
|
||||
@@ -115,7 +133,7 @@ namespace Barotrauma
|
||||
foreach (var monster in monsters)
|
||||
{
|
||||
monster.Enabled = false;
|
||||
if (monster.Params.AI.EnforceAggressiveBehaviorForMissions)
|
||||
if (monster.Params.AI != null && monster.Params.AI.EnforceAggressiveBehaviorForMissions)
|
||||
{
|
||||
foreach (var targetParam in monster.Params.AI.Targets)
|
||||
{
|
||||
@@ -203,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)))
|
||||
{
|
||||
level.LevelData.HasHuntingGrounds = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEliminated(Character enemy) =>
|
||||
|
||||
@@ -20,7 +20,9 @@ namespace Barotrauma
|
||||
|
||||
private readonly float itemSpawnRadius = 800.0f;
|
||||
private readonly float approachItemsRadius = 1000.0f;
|
||||
private readonly float nestObjectRadius = 1000.0f;
|
||||
private readonly float monsterSpawnRadius = 3000.0f;
|
||||
private readonly int nestObjectAmount = 10;
|
||||
|
||||
private readonly bool requireDelivery;
|
||||
|
||||
@@ -33,7 +35,14 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return nestPosition;
|
||||
if (State > 0)
|
||||
{
|
||||
Enumerable.Empty<Vector2>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return nestPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +55,9 @@ namespace Barotrauma
|
||||
approachItemsRadius = prefab.ConfigElement.GetAttributeFloat("approachitemsradius", itemSpawnRadius * 2.0f);
|
||||
monsterSpawnRadius = prefab.ConfigElement.GetAttributeFloat("monsterspawnradius", approachItemsRadius * 2.0f);
|
||||
|
||||
nestObjectRadius = prefab.ConfigElement.GetAttributeFloat("nestobjectradius", itemSpawnRadius * 2.0f);
|
||||
nestObjectAmount = prefab.ConfigElement.GetAttributeInt("nestobjectamount", 10);
|
||||
|
||||
requireDelivery = prefab.ConfigElement.GetAttributeBool("requiredelivery", false);
|
||||
|
||||
string spawnPositionTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||
@@ -55,7 +67,6 @@ namespace Barotrauma
|
||||
spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin;
|
||||
}
|
||||
|
||||
|
||||
foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster"))
|
||||
{
|
||||
string speciesName = monsterElement.GetAttributeString("character", string.Empty);
|
||||
@@ -79,8 +90,18 @@ namespace Barotrauma
|
||||
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (items.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"items.Count > 0 ({items.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Item list was not empty at the start of a nest mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
items.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!IsClient)
|
||||
{
|
||||
//ruin/cave/wreck items are allowed to spawn close to the sub
|
||||
@@ -90,6 +111,25 @@ namespace Barotrauma
|
||||
List<GraphEdge> spawnEdges = new List<GraphEdge>();
|
||||
if (spawnPositionType == Level.PositionType.Cave)
|
||||
{
|
||||
Level.Cave closestCave = null;
|
||||
float closestCaveDist = float.PositiveInfinity;
|
||||
foreach (var cave in Level.Loaded.Caves)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(nestPosition, cave.Area.Center.ToVector2());
|
||||
if (dist < closestCaveDist)
|
||||
{
|
||||
closestCave = cave;
|
||||
closestCaveDist = dist;
|
||||
}
|
||||
}
|
||||
if (closestCave != null)
|
||||
{
|
||||
closestCave.DisplayOnSonar = true;
|
||||
SpawnNestObjects(level, closestCave);
|
||||
#if SERVER
|
||||
selectedCave = closestCave;
|
||||
#endif
|
||||
}
|
||||
var nearbyCells = Level.Loaded.GetCells(nestPosition, searchDepth: 3);
|
||||
if (nearbyCells.Any())
|
||||
{
|
||||
@@ -171,6 +211,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnNestObjects(Level level, Level.Cave cave)
|
||||
{
|
||||
level.LevelObjectManager.PlaceNestObjects(level, cave, nestPosition, nestObjectRadius, nestObjectAmount);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (IsClient)
|
||||
@@ -258,9 +303,17 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (!AllItemsDestroyedOrRetrieved())
|
||||
if (AllItemsDestroyedOrRetrieved())
|
||||
{
|
||||
return;
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Item item in items)
|
||||
{
|
||||
@@ -270,8 +323,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
items.Clear();
|
||||
GiveReward();
|
||||
completed = true;
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -101,7 +102,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
#if SERVER
|
||||
originalInventoryID = Entity.NullEntityID;
|
||||
@@ -168,10 +169,11 @@ namespace Barotrauma
|
||||
//try to find a container and place the item inside it
|
||||
if (!string.IsNullOrEmpty(containerTag) && item.ParentInventory == null)
|
||||
{
|
||||
List<ItemContainer> validContainers = new List<ItemContainer>();
|
||||
foreach (Item it in Item.ItemList)
|
||||
{
|
||||
if (!it.HasTag(containerTag)) { continue; }
|
||||
if (it.NonInteractable) { continue; }
|
||||
if (!it.IsPlayerTeamInteractable) { continue; }
|
||||
switch (spawnPositionType)
|
||||
{
|
||||
case Level.PositionType.Cave:
|
||||
@@ -185,15 +187,18 @@ namespace Barotrauma
|
||||
if (it.Submarine == null || it.Submarine.Info.Type != SubmarineType.Wreck) { continue; }
|
||||
break;
|
||||
}
|
||||
var itemContainer = it.GetComponent<Items.Components.ItemContainer>();
|
||||
if (itemContainer == null) { continue; }
|
||||
if (itemContainer.Combine(item, user: null))
|
||||
var itemContainer = it.GetComponent<ItemContainer>();
|
||||
if (itemContainer != null && itemContainer.Inventory.CanBePut(item)) { validContainers.Add(itemContainer); }
|
||||
}
|
||||
if (validContainers.Any())
|
||||
{
|
||||
var selectedContainer = validContainers.GetRandom();
|
||||
if (selectedContainer.Combine(item, user: null))
|
||||
{
|
||||
#if SERVER
|
||||
originalInventoryID = it.ID;
|
||||
originalItemContainerIndex = (byte)it.GetComponentIndex(itemContainer);
|
||||
originalInventoryID = selectedContainer.Item.ID;
|
||||
originalItemContainerIndex = (byte)selectedContainer.Item.GetComponentIndex(selectedContainer);
|
||||
#endif
|
||||
break;
|
||||
} // Placement successful
|
||||
}
|
||||
}
|
||||
@@ -248,6 +253,11 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
|
||||
item?.Remove();
|
||||
item = null;
|
||||
GiveReward();
|
||||
|
||||
@@ -16,8 +16,6 @@ namespace Barotrauma
|
||||
private readonly float scatter;
|
||||
private readonly float offset;
|
||||
|
||||
private readonly bool spawnDeep;
|
||||
|
||||
private Vector2? spawnPos;
|
||||
|
||||
private readonly bool disallowed;
|
||||
@@ -73,14 +71,18 @@ namespace Barotrauma
|
||||
maxAmount = Math.Max(prefab.ConfigElement.GetAttributeInt("maxamount", 1), minAmount);
|
||||
|
||||
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);
|
||||
|
||||
@@ -138,6 +140,11 @@ namespace Barotrauma
|
||||
var removals = new List<Level.InterestingPosition>();
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
if (SpawnPosFilter != null && !SpawnPosFilter(position))
|
||||
{
|
||||
removals.Add(position);
|
||||
continue;
|
||||
}
|
||||
if (position.Submarine != null)
|
||||
{
|
||||
if (position.Submarine.WreckAI != null && position.Submarine.WreckAI.IsAlive)
|
||||
@@ -154,19 +161,10 @@ namespace Barotrauma
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Level.Loaded.ExtraWalls.Any(w => w.Cells.Any(c => c.IsPointInside(position.Position.ToVector2()))))
|
||||
if (Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(position.Position.ToVector2())))
|
||||
{
|
||||
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);
|
||||
@@ -180,33 +178,36 @@ namespace Barotrauma
|
||||
{
|
||||
if (disallowed) { return; }
|
||||
|
||||
if (Rand.Value(Rand.RandSync.Server) > prefab.SpawnProbability)
|
||||
{
|
||||
spawnPos = null;
|
||||
Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
spawnPos = Vector2.Zero;
|
||||
var availablePositions = GetAvailableSpawnPositions();
|
||||
var chosenPosition = new Level.InterestingPosition(Point.Zero, Level.PositionType.MainPath, isValid: false);
|
||||
var removedPositions = new List<Level.InterestingPosition>();
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
if (Rand.Value(Rand.RandSync.Server) > prefab.SpawnProbability)
|
||||
{
|
||||
removedPositions.Add(position);
|
||||
}
|
||||
}
|
||||
removedPositions.ForEach(p => availablePositions.Remove(p));
|
||||
bool isSubOrWreck = spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Wreck;
|
||||
if (affectSubImmediately && !isSubOrWreck)
|
||||
if (affectSubImmediately && !isSubOrWreck && spawnPosType != Level.PositionType.Abyss)
|
||||
{
|
||||
if (availablePositions.None())
|
||||
{
|
||||
//no suitable position found, disable the event
|
||||
spawnPos = null;
|
||||
Finished();
|
||||
return;
|
||||
}
|
||||
Submarine refSub = GetReferenceSub();
|
||||
if (Submarine.MainSubs.Length == 2 && Submarine.MainSubs[1] != null)
|
||||
{
|
||||
refSub = Submarine.MainSubs.GetRandom(Rand.RandSync.Unsynced);
|
||||
}
|
||||
float closestDist = float.PositiveInfinity;
|
||||
//find the closest spawnposition that isn't too close to any of the subs
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
Vector2 pos = position.Position.ToVector2();
|
||||
Submarine refSub = GetReferenceSub();
|
||||
float dist = Vector2.DistanceSquared(pos, refSub.WorldPosition);
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
@@ -248,7 +249,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(position.Position.ToVector2(), GetReferenceSub().WorldPosition);
|
||||
float dist = Vector2.DistanceSquared(position.Position.ToVector2(), refSub.WorldPosition);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
@@ -262,11 +263,21 @@ namespace Barotrauma
|
||||
if (!isSubOrWreck)
|
||||
{
|
||||
float minDistance = 20000;
|
||||
availablePositions.RemoveAll(p => Vector2.DistanceSquared(GetReferenceSub().WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
|
||||
var refSub = GetReferenceSub();
|
||||
availablePositions.RemoveAll(p => Vector2.DistanceSquared(refSub.WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
|
||||
if (Submarine.MainSubs.Length > 1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (availablePositions.None())
|
||||
{
|
||||
//no suitable position found, disable the event
|
||||
spawnPos = null;
|
||||
Finished();
|
||||
return;
|
||||
}
|
||||
@@ -335,6 +346,8 @@ namespace Barotrauma
|
||||
if (spawnPos == null)
|
||||
{
|
||||
FindSpawnPosition(affectSubImmediately: true);
|
||||
//the event gets marked as finished if a spawn point is not found
|
||||
if (isFinished) { return; }
|
||||
spawnPending = true;
|
||||
}
|
||||
|
||||
@@ -342,7 +355,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)
|
||||
{
|
||||
@@ -381,6 +394,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 > Level.Loaded.AbyssStart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spawnPending = false;
|
||||
|
||||
//+1 because Range returns an integer less than the max value
|
||||
@@ -412,7 +438,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)
|
||||
{
|
||||
@@ -421,7 +456,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,6 +13,9 @@ namespace Barotrauma
|
||||
private int prevEntityCount;
|
||||
private int prevPlayerCount, prevBotCount;
|
||||
|
||||
private readonly string[] requiredDestinationTypes;
|
||||
public readonly bool RequireBeaconStation;
|
||||
|
||||
public int CurrentActionIndex { get; private set; }
|
||||
public List<EventAction> Actions { get; } = new List<EventAction>();
|
||||
public Dictionary<string, List<Entity>> Targets { get; } = new Dictionary<string, List<Entity>>();
|
||||
@@ -39,6 +42,9 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError($"Scripted event \"{prefab.Identifier}\" has no actions. The event will do nothing.");
|
||||
}
|
||||
|
||||
requiredDestinationTypes = prefab.ConfigElement.GetAttributeStringArray("requireddestinationtypes", null);
|
||||
RequireBeaconStation = prefab.ConfigElement.GetAttributeBool("requirebeaconstation", false);
|
||||
}
|
||||
|
||||
public void AddTarget(string tag, Entity target)
|
||||
@@ -199,5 +205,21 @@ namespace Barotrauma
|
||||
currentAction.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool LevelMeetsRequirements()
|
||||
{
|
||||
if (requiredDestinationTypes == null) { return true; }
|
||||
var currLocation = GameMain.GameSession?.Campaign?.Map.CurrentLocation;
|
||||
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