Release 1.9.7.0 - Summer Update 2025
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -30,6 +30,9 @@ namespace Barotrauma
|
||||
[Serialize(120.0f, IsPropertySaveable.Yes, description: "How long it takes for the NPC to \"cool down\" (stop attacking).")]
|
||||
public float CoolDown { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "The event actions reset when a GoTo action makes the event jump to a different point. Should the NPC revert back to a normal state when the event resets?")]
|
||||
public bool AbandonOnReset { get; set; }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
|
||||
@@ -81,7 +84,7 @@ namespace Barotrauma
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
if (affectedNpcs != null)
|
||||
if (affectedNpcs != null && AbandonOnReset)
|
||||
{
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Barotrauma
|
||||
namespace Barotrauma
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes a specific character invulnerable to damage and unable to die.
|
||||
@@ -36,13 +36,21 @@ namespace Barotrauma
|
||||
{
|
||||
if (target != null && target is Character character)
|
||||
{
|
||||
if (UpdateAfflictions)
|
||||
if (Enabled)
|
||||
{
|
||||
character.CharacterHealth.Unkillable = Enabled;
|
||||
if (UpdateAfflictions)
|
||||
{
|
||||
character.CharacterHealth.Unkillable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
character.GodMode = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character.GodMode = Enabled;
|
||||
character.CharacterHealth.Unkillable = false;
|
||||
character.GodMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -14,7 +14,10 @@ namespace Barotrauma
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Should the NPC start or stop waiting?")]
|
||||
public bool Wait { get; set; }
|
||||
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "The event actions reset when a GoTo action makes the event jump to a different point. Should the NPC stop waiting when the event resets?")]
|
||||
public bool AbandonOnReset { get; set; }
|
||||
|
||||
[Serialize(AIObjectiveManager.MaxObjectivePriority, IsPropertySaveable.Yes, description: "AI priority for the action. Uses 100 by default, which is the absolute maximum for any objectives, " +
|
||||
"meaning nothing can be prioritized over it, including the emergency objectives, such as find safety and combat." +
|
||||
"Setting the priority to 70 would function like a regular order, but with the highest priority." +
|
||||
@@ -76,7 +79,7 @@ namespace Barotrauma
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
if (affectedNpcs != null)
|
||||
if (affectedNpcs != null && AbandonOnReset)
|
||||
{
|
||||
foreach (var npc in affectedNpcs)
|
||||
{
|
||||
|
||||
@@ -367,7 +367,7 @@ namespace Barotrauma
|
||||
SpawnType? spawnPointType = null;
|
||||
if (!ignoreSpawnPointType) { spawnPointType = SpawnPointType; }
|
||||
|
||||
return GetSpawnPos(SpawnLocation, spawnPointType, targetModuleTags, SpawnPointTag.ToEnumerable(), requireTaggedSpawnPoint: RequireSpawnPointTag, allowInPlayerView: AllowInPlayerView);
|
||||
return GetSpawnPos(SpawnLocation, spawnPointType, targetModuleTags, SpawnPointTag.IsEmpty ? null : SpawnPointTag.ToEnumerable(), requireTaggedSpawnPoint: RequireSpawnPointTag, allowInPlayerView: AllowInPlayerView);
|
||||
}
|
||||
|
||||
private static bool IsValidSubmarineType(SpawnLocationType spawnLocation, Submarine submarine)
|
||||
@@ -422,12 +422,32 @@ namespace Barotrauma
|
||||
}
|
||||
if (spawnpointTags != null && spawnpointTags.Any())
|
||||
{
|
||||
var spawnPoints = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag) && wp.ConnectedDoor == null && wp.IsTraversable));
|
||||
if (requireTaggedSpawnPoint || spawnPoints.Any())
|
||||
var spawnPointsWithTag = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag) && wp.ConnectedDoor == null && wp.IsTraversable));
|
||||
if (requireTaggedSpawnPoint || spawnPointsWithTag.Any())
|
||||
{
|
||||
potentialSpawnPoints = spawnPoints.ToList();
|
||||
potentialSpawnPoints = spawnPointsWithTag.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
//no spawnpoints with the tag we want -> choose something with no tags
|
||||
TryGetSpawnPointsWithNoTag();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if no tags are specified, prefer a spawnpoint with no tags, i.e. prefer a "generic" spawnpoint instead of some special one like a jail spawnpoint
|
||||
TryGetSpawnPointsWithNoTag();
|
||||
}
|
||||
|
||||
void TryGetSpawnPointsWithNoTag()
|
||||
{
|
||||
var spawnPointsWithNoTag = potentialSpawnPoints.Where(wp => wp.Tags.None());
|
||||
if (spawnPointsWithNoTag.Any())
|
||||
{
|
||||
potentialSpawnPoints = spawnPointsWithNoTag.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
if (potentialSpawnPoints.None())
|
||||
{
|
||||
if (requireTaggedSpawnPoint && spawnpointTags != null && spawnpointTags.Any())
|
||||
|
||||
@@ -777,7 +777,15 @@ namespace Barotrauma
|
||||
var locationType = location.GetLocationTypeToDisplay();
|
||||
bool includeGenericEvents = level.Type == LevelData.LevelType.LocationConnection || !locationType.IgnoreGenericEvents;
|
||||
if (includeGenericEvents && eventSet.LocationTypeIdentifiers == null) { return true; }
|
||||
return eventSet.LocationTypeIdentifiers != null && eventSet.LocationTypeIdentifiers.Any(identifier => identifier == locationType.Identifier);
|
||||
if (eventSet.LocationTypeIdentifiers == null) { return false; }
|
||||
|
||||
// EventLocationType is used to have the event set consider the location id as something else, for example "city" to get events that go to city locations
|
||||
bool hasMatchingEventLocationId = !locationType.EventLocationType.IsEmpty &&
|
||||
eventSet.LocationTypeIdentifiers.Contains(locationType.EventLocationType);
|
||||
|
||||
bool hasMatchingLocationId = eventSet.LocationTypeIdentifiers.Contains(locationType.Identifier);
|
||||
|
||||
return hasMatchingEventLocationId || hasMatchingLocationId;
|
||||
}
|
||||
|
||||
private Location GetEventLocation()
|
||||
|
||||
@@ -156,9 +156,34 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int previousKillTargetsRemaining = -1;
|
||||
|
||||
private void TrackKillTargetCount()
|
||||
{
|
||||
if (requireKill.Count == 0) { return; }
|
||||
|
||||
if (previousKillTargetsRemaining == -1)
|
||||
{
|
||||
previousKillTargetsRemaining = requireKill.Count();
|
||||
}
|
||||
|
||||
int killTargetsRemaining = requireKill.Count(c => !c.Removed && !c.IsDead && !(c.LockHands && c.Submarine == Submarine.MainSub));
|
||||
// at least one of the targets have been eliminated
|
||||
if (killTargetsRemaining < previousKillTargetsRemaining)
|
||||
{
|
||||
#if CLIENT
|
||||
SteamTimelineManager.OnOutpostTargetEliminated(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
previousKillTargetsRemaining = killTargetsRemaining;
|
||||
}
|
||||
|
||||
protected override void UpdateMissionSpecific(float deltaTime)
|
||||
{
|
||||
TrackKillTargetCount();
|
||||
|
||||
if (State != HostagesKilledState)
|
||||
{
|
||||
if (requireRescue.Any(r => r.Removed || r.IsDead))
|
||||
|
||||
@@ -9,14 +9,30 @@ namespace Barotrauma
|
||||
{
|
||||
partial class EscortMission : Mission
|
||||
{
|
||||
private readonly ContentXElement itemConfig;
|
||||
private readonly ContentXElement terroristItemConfig;
|
||||
|
||||
private readonly Dictionary<HumanPrefab, List<StatusEffect>> characterStatusEffects = new Dictionary<HumanPrefab, List<StatusEffect>>();
|
||||
|
||||
/// <summary>
|
||||
/// Number of escorted characters by default.
|
||||
/// </summary>
|
||||
private readonly int baseEscortedCharacters;
|
||||
/// <summary>
|
||||
/// A scaling factor for the number of escorted characters, relative to the recommended crew size of the sub. The total amount of escorted characters is calculated as
|
||||
/// baseEscortedCharacters + scalingEscortedCharacters * (RecommendedCrewSizeMin + RecommendedCrewSizeMax) / 2
|
||||
/// </summary>
|
||||
private readonly float scalingEscortedCharacters;
|
||||
/// <summary>
|
||||
/// The probability for the escorted characters to be "terrorists" (turning them hostile when the sub has progressed enough in the level).
|
||||
/// A value of 0.5 would mean about half of the characters are terrorist, 1 would mean they all are. There's 20% of randomness applied to the value to make it less predictable.
|
||||
/// </summary>
|
||||
private readonly float terroristChance;
|
||||
|
||||
/// <summary>
|
||||
/// Dialog tag the terrorists use in their dialog when they become hostile.
|
||||
/// </summary>
|
||||
private readonly string terroristAnnounceDialogTag;
|
||||
|
||||
private int calculatedReward;
|
||||
private Submarine missionSub;
|
||||
|
||||
@@ -26,7 +42,6 @@ namespace Barotrauma
|
||||
private bool terroristsShouldAct = false;
|
||||
private float terroristDistanceSquared;
|
||||
private const string TerroristTeamChangeIdentifier = "terrorist";
|
||||
private readonly string terroristAnnounceDialogTag = string.Empty;
|
||||
|
||||
public EscortMission(MissionPrefab prefab, Location[] locations, Submarine sub)
|
||||
: base(prefab, locations, sub)
|
||||
@@ -35,8 +50,10 @@ namespace Barotrauma
|
||||
baseEscortedCharacters = prefab.ConfigElement.GetAttributeInt("baseescortedcharacters", 1);
|
||||
scalingEscortedCharacters = prefab.ConfigElement.GetAttributeFloat("scalingescortedcharacters", 0);
|
||||
terroristChance = prefab.ConfigElement.GetAttributeFloat("terroristchance", 0);
|
||||
itemConfig = prefab.ConfigElement.GetChildElement("TerroristItems");
|
||||
terroristAnnounceDialogTag = prefab.ConfigElement.GetAttributeString("terroristannouncedialogtag", string.Empty);
|
||||
terroristItemConfig = prefab.ConfigElement.GetChildElement("TerroristItems");
|
||||
terroristAnnounceDialogTag =
|
||||
prefab.ConfigElement.GetAttributeString("dialogterroristannounce",
|
||||
prefab.ConfigElement.GetAttributeString("terroristAnnounceDialogTag", string.Empty));
|
||||
CalculateReward();
|
||||
}
|
||||
|
||||
@@ -94,35 +111,29 @@ namespace Barotrauma
|
||||
randSync = Rand.RandSync.Unsynced;
|
||||
}
|
||||
|
||||
List<HumanPrefab> humanPrefabsToSpawn = new List<HumanPrefab>();
|
||||
List<(HumanPrefab humanPrefab, List<StatusEffect> statusEffects)> humanPrefabsToSpawn = new List<(HumanPrefab humanPrefab, List<StatusEffect> statusEffects)>();
|
||||
foreach (ContentXElement characterElement in characterConfig.Elements())
|
||||
{
|
||||
int count = CalculateScalingEscortedCharacterCount(inMission: true);
|
||||
var humanPrefab = GetHumanPrefabFromElement(characterElement);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
humanPrefabsToSpawn.Add(humanPrefab);
|
||||
}
|
||||
foreach (var element in characterElement.Elements())
|
||||
{
|
||||
if (element.NameAsIdentifier() == "statuseffect")
|
||||
List<StatusEffect> characterStatusEffects = new List<StatusEffect>();
|
||||
foreach (var element in characterElement.Elements())
|
||||
{
|
||||
var newEffect = StatusEffect.Load(element, parentDebugName: Prefab.Name.Value);
|
||||
if (newEffect == null) { continue; }
|
||||
if (!characterStatusEffects.ContainsKey(humanPrefab))
|
||||
if (element.NameAsIdentifier() == "statuseffect")
|
||||
{
|
||||
characterStatusEffects[humanPrefab] = new List<StatusEffect> { newEffect };
|
||||
var newEffect = StatusEffect.Load(element, parentDebugName: Prefab.Name.Value);
|
||||
if (newEffect == null) { continue; }
|
||||
characterStatusEffects.Add(newEffect);
|
||||
}
|
||||
else
|
||||
{
|
||||
characterStatusEffects[humanPrefab].Add(newEffect);
|
||||
}
|
||||
}
|
||||
humanPrefabsToSpawn.Add((humanPrefab, characterStatusEffects));
|
||||
}
|
||||
}
|
||||
|
||||
//if any of the escortees have a job defined, try to use a spawnpoint designated for that job
|
||||
foreach (var humanPrefab in humanPrefabsToSpawn)
|
||||
foreach ((var humanPrefab, var statusEffectList) in humanPrefabsToSpawn)
|
||||
{
|
||||
if (humanPrefab == null || humanPrefab.Job.IsEmpty || humanPrefab.Job == "any") { continue; }
|
||||
var jobPrefab = humanPrefab.GetJobPrefab(randSync);
|
||||
@@ -136,23 +147,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var humanPrefab in humanPrefabsToSpawn)
|
||||
foreach ((var humanPrefab, var statusEffectList) in humanPrefabsToSpawn)
|
||||
{
|
||||
Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, Submarine.MainSub, CharacterTeamType.FriendlyNPC, explicitStayInHullPos, humanPrefabRandSync: randSync);
|
||||
if (spawnedCharacter.AIController is HumanAIController humanAI)
|
||||
{
|
||||
humanAI.InitMentalStateManager();
|
||||
}
|
||||
if (characterStatusEffects.TryGetValue(humanPrefab, out var statusEffectList))
|
||||
foreach (var statusEffect in statusEffectList)
|
||||
{
|
||||
foreach (var statusEffect in statusEffectList)
|
||||
{
|
||||
statusEffect.Apply(statusEffect.type, 1.0f, spawnedCharacter, spawnedCharacter);
|
||||
}
|
||||
}
|
||||
statusEffect.Apply(statusEffect.type, 1.0f, spawnedCharacter, spawnedCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (terroristChance > 0f)
|
||||
{
|
||||
int terroristCount = (int)Math.Ceiling(terroristChance * Rand.Range(0.8f, 1.2f) * characters.Count);
|
||||
@@ -256,13 +263,23 @@ namespace Barotrauma
|
||||
character.TryAddNewTeamChange(TerroristTeamChangeIdentifier, new ActiveTeamChange(CharacterTeamType.None, ActiveTeamChange.TeamChangePriorities.Willful, aggressiveBehavior: true));
|
||||
if (!string.IsNullOrEmpty(terroristAnnounceDialogTag))
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogterroristannounce").Value, null, Rand.Range(0.5f, 3f));
|
||||
character.Speak(TextManager.Get(terroristAnnounceDialogTag).Value, null, Rand.Range(0.5f, 3f));
|
||||
}
|
||||
ContentXElement randomElement = itemConfig.Elements().GetRandomUnsynced(e => e.GetAttributeFloat(0f, "mindifficulty") <= Level.Loaded.Difficulty);
|
||||
if (randomElement != null)
|
||||
foreach (var itemElement in terroristItemConfig.Elements())
|
||||
{
|
||||
HumanPrefab.InitializeItem(character, randomElement, character.Submarine, humanPrefab: null, createNetworkEvents: true);
|
||||
float levelDifficulty = Level.Loaded?.Difficulty ?? 0.0f;
|
||||
var selectedItemElement = itemElement;
|
||||
if (itemElement.NameAsIdentifier() == "chooserandom".ToIdentifier())
|
||||
{
|
||||
selectedItemElement = itemElement.Elements().GetRandomUnsynced(e => e.GetAttributeFloat(0f, "mindifficulty") <= levelDifficulty);
|
||||
}
|
||||
if (selectedItemElement != null)
|
||||
{
|
||||
if (levelDifficulty < selectedItemElement.GetAttributeFloat(0f, "mindifficulty")) { continue; }
|
||||
HumanPrefab.InitializeItem(character, selectedItemElement, character.Submarine, humanPrefab: null, createNetworkEvents: true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (state != value)
|
||||
{
|
||||
int previousState = state;
|
||||
state = value;
|
||||
TryTriggerEvents(state);
|
||||
#if SERVER
|
||||
@@ -38,6 +39,7 @@ namespace Barotrauma
|
||||
#endif
|
||||
ShowMessage(State);
|
||||
OnMissionStateChanged?.Invoke(this);
|
||||
MissionStateChanged(previousState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,6 +200,11 @@ namespace Barotrauma
|
||||
Messages = messages.ToImmutableArray();
|
||||
|
||||
characterConfig = prefab.ConfigElement.GetChildElement("Characters");
|
||||
if (prefab.ConfigElement.GetChildElements("Characters").Count() > 1)
|
||||
{
|
||||
DebugConsole.AddWarning($"Error in mission {Prefab.Identifier}: multiple <Characters> elements found. Only the first one will be used.",
|
||||
contentPackage: prefab.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
public LocalizedString ReplaceVariablesInMissionMessage(LocalizedString message, Submarine sub, bool replaceReward = true)
|
||||
@@ -214,6 +221,8 @@ namespace Barotrauma
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
protected virtual void MissionStateChanged(int previousState) {}
|
||||
|
||||
public virtual void SetLevel(LevelData level) { }
|
||||
|
||||
|
||||
@@ -109,6 +109,8 @@ namespace Barotrauma
|
||||
|
||||
public readonly bool AllowRetry;
|
||||
|
||||
public readonly bool ShowSonarLabels;
|
||||
|
||||
public readonly bool ShowInMenus, ShowStartMessage;
|
||||
|
||||
public readonly bool IsSideObjective;
|
||||
@@ -219,11 +221,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
Reward = element.GetAttributeInt("reward", 1);
|
||||
ExperienceMultiplier = element.GetAttributeFloat("experiencemultiplier", 1.0f);
|
||||
AllowRetry = element.GetAttributeBool("allowretry", false);
|
||||
ShowInMenus = element.GetAttributeBool("showinmenus", true);
|
||||
ShowStartMessage = element.GetAttributeBool("showstartmessage", true);
|
||||
Reward = element.GetAttributeInt(nameof(Reward), 1);
|
||||
ExperienceMultiplier = element.GetAttributeFloat(nameof(ExperienceMultiplier), 1.0f);
|
||||
AllowRetry = element.GetAttributeBool(nameof(AllowRetry), false);
|
||||
ShowSonarLabels = element.GetAttributeBool(nameof(ShowSonarLabels), true);
|
||||
ShowInMenus = element.GetAttributeBool(nameof(ShowInMenus), true);
|
||||
ShowStartMessage = element.GetAttributeBool(nameof(ShowStartMessage), true);
|
||||
IsSideObjective = element.GetAttributeBool("sideobjective", false);
|
||||
|
||||
RequireWreck = element.GetAttributeBool(nameof(RequireWreck), false);
|
||||
@@ -237,11 +240,12 @@ namespace Barotrauma
|
||||
|
||||
BlockLocationTypeChanges = element.GetAttributeBool(nameof(BlockLocationTypeChanges), false);
|
||||
RequiredLocationFaction = element.GetAttributeIdentifier(nameof(RequiredLocationFaction), Identifier.Empty);
|
||||
Commonness = element.GetAttributeInt("commonness", 1);
|
||||
AllowOtherMissionsInLevel = element.GetAttributeBool("allowothermissionsinlevel", true);
|
||||
Commonness = element.GetAttributeInt(nameof(Commonness), 1);
|
||||
AllowOtherMissionsInLevel = element.GetAttributeBool(nameof(AllowOtherMissionsInLevel), true);
|
||||
|
||||
if (element.GetAttribute("difficulty") != null)
|
||||
{
|
||||
int difficulty = element.GetAttributeInt("difficulty", MinDifficulty);
|
||||
int difficulty = element.GetAttributeInt(nameof(Difficulty), MinDifficulty);
|
||||
Difficulty = Math.Clamp(difficulty, MinDifficulty, MaxDifficulty);
|
||||
}
|
||||
MinLevelDifficulty = element.GetAttributeInt(nameof(MinLevelDifficulty), MinLevelDifficulty);
|
||||
|
||||
@@ -173,6 +173,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected override void MissionStateChanged(int previousState)
|
||||
{
|
||||
// state of 1+ here means the mission is completed
|
||||
if (previousState == 0 && State >= 1)
|
||||
{
|
||||
#if CLIENT
|
||||
SteamTimelineManager.OnMonsterMissionTargetsKilled(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateMissionSpecific(float deltaTime)
|
||||
{
|
||||
switch (State)
|
||||
|
||||
@@ -92,13 +92,38 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
if (value == state) { return; }
|
||||
bool wasRetrieved = Retrieved;
|
||||
state = value;
|
||||
#if SERVER
|
||||
GameMain.Server?.UpdateMissionState(mission);
|
||||
#endif
|
||||
if (!wasRetrieved && Retrieved)
|
||||
{
|
||||
OnTargetRetrieved();
|
||||
}
|
||||
else if (state == RetrievalState.PickedUp)
|
||||
{
|
||||
OnTargetPickedUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTargetRetrieved()
|
||||
{
|
||||
if (Item == null) { return; }
|
||||
#if CLIENT
|
||||
SteamTimelineManager.OnMissionTargetRetrieved(Item, mission);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnTargetPickedUp()
|
||||
{
|
||||
if (Item == null) { return; }
|
||||
#if CLIENT
|
||||
SteamTimelineManager.OnMissionTargetPickedUp(Item, mission);
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool Interacted;
|
||||
|
||||
private readonly SalvageMission mission;
|
||||
@@ -469,13 +494,13 @@ namespace Barotrauma
|
||||
target.Item.ExternalHighlight = true;
|
||||
#endif
|
||||
target.Item.UpdateTransform();
|
||||
if (target.Item.CurrentHull == null)
|
||||
if (target.Item.CurrentHull == null && target.Item.body != null)
|
||||
{
|
||||
//prevent the body from moving if it spawned outside the hulls (we don't want it e.g. falling to the bottom of a cave or into the abyss)
|
||||
target.Item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
}
|
||||
}
|
||||
else if (target.RequiredRetrievalState == Target.RetrievalState.Interact)
|
||||
if (target.RequiredRetrievalState == Target.RetrievalState.Interact)
|
||||
{
|
||||
target.Item.OnInteract += () =>
|
||||
{
|
||||
|
||||
@@ -168,6 +168,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected override void MissionStateChanged(int previousState)
|
||||
{
|
||||
// detect successful scanned targets increasing after scan is completed
|
||||
if (previousState < State)
|
||||
{
|
||||
#if CLIENT
|
||||
SteamTimelineManager.OnScanSuccessful(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void GetScanners()
|
||||
{
|
||||
foreach (var startingItem in startingItems)
|
||||
|
||||
Reference in New Issue
Block a user