Build 0.18.2.0
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -275,7 +275,11 @@ namespace Barotrauma
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "chooserandom":
|
||||
LoadSubElement(subElement.Elements().ToArray().GetRandom(random));
|
||||
var subElements = subElement.Elements();
|
||||
if (subElements.Any())
|
||||
{
|
||||
LoadSubElement(subElements.ToArray().GetRandom(random));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LoadSubElement(subElement);
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Barotrauma
|
||||
|
||||
public readonly Identifier[] ForbiddenAmmunition;
|
||||
|
||||
public static WreckAIConfig GetRandom() => Prefabs.GetRandom(Rand.RandSync.ServerAndClient);
|
||||
public static WreckAIConfig GetRandom() => Prefabs.OrderBy(p => p.UintIdentifier).GetRandom(Rand.RandSync.ServerAndClient);
|
||||
|
||||
protected override Identifier DetermineIdentifier(XElement element)
|
||||
{
|
||||
|
||||
@@ -94,11 +94,67 @@ namespace Barotrauma
|
||||
|
||||
public Vector2 SheetIndex => Preset.SheetIndex;
|
||||
|
||||
public ContentXElement HairElement => CharacterInfo.Hairs?.ElementAtOrDefault(HairIndex);
|
||||
public ContentXElement HairWithHatElement => CharacterInfo.Hairs?.ElementAtOrDefault(HairWithHatIndex);
|
||||
public ContentXElement BeardElement => CharacterInfo.Beards?.ElementAtOrDefault(BeardIndex);
|
||||
public ContentXElement MoustacheElement => CharacterInfo.Moustaches?.ElementAtOrDefault(MoustacheIndex);
|
||||
public ContentXElement FaceAttachment => CharacterInfo.FaceAttachments?.ElementAtOrDefault(FaceAttachmentIndex);
|
||||
public ContentXElement HairElement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CharacterInfo.Hairs == null) { return null; }
|
||||
if (hairIndex >= CharacterInfo.Hairs.Count)
|
||||
{
|
||||
DebugConsole.AddWarning($"Hair index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {hairIndex})");
|
||||
}
|
||||
return CharacterInfo.Hairs.ElementAtOrDefault(hairIndex);
|
||||
}
|
||||
}
|
||||
public ContentXElement HairWithHatElement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CharacterInfo.Hairs == null) { return null; }
|
||||
if (HairWithHatIndex >= CharacterInfo.Hairs.Count)
|
||||
{
|
||||
DebugConsole.AddWarning($"Hair with hat index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {HairWithHatIndex})");
|
||||
}
|
||||
return CharacterInfo.Hairs.ElementAtOrDefault(HairWithHatIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public ContentXElement BeardElement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CharacterInfo.Beards == null) { return null; }
|
||||
if (BeardIndex >= CharacterInfo.Beards.Count)
|
||||
{
|
||||
DebugConsole.AddWarning($"Beard index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {BeardIndex})");
|
||||
}
|
||||
return CharacterInfo.Beards.ElementAtOrDefault(BeardIndex);
|
||||
}
|
||||
}
|
||||
public ContentXElement MoustacheElement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CharacterInfo.Moustaches == null) { return null; }
|
||||
if (MoustacheIndex >= CharacterInfo.Moustaches.Count)
|
||||
{
|
||||
DebugConsole.AddWarning($"Moustache index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {MoustacheIndex})");
|
||||
}
|
||||
return CharacterInfo.Moustaches.ElementAtOrDefault(MoustacheIndex);
|
||||
}
|
||||
}
|
||||
public ContentXElement FaceAttachment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CharacterInfo.FaceAttachments == null) { return null; }
|
||||
if (FaceAttachmentIndex >= CharacterInfo.FaceAttachments.Count)
|
||||
{
|
||||
DebugConsole.AddWarning($"Face attachment index out of range (character: {CharacterInfo?.Name ?? "null"}, index: {FaceAttachmentIndex})");
|
||||
}
|
||||
return CharacterInfo.FaceAttachments.ElementAtOrDefault(FaceAttachmentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public HeadInfo(CharacterInfo characterInfo, HeadPreset headPreset, int hairIndex = 0, int beardIndex = 0, int moustacheIndex = 0, int faceAttachmentIndex = 0)
|
||||
{
|
||||
@@ -130,6 +186,10 @@ namespace Barotrauma
|
||||
head = value;
|
||||
HeadSprite = null;
|
||||
AttachmentSprites = null;
|
||||
hairs = null;
|
||||
beards = null;
|
||||
moustaches = null;
|
||||
faceAttachments = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -843,7 +903,14 @@ namespace Barotrauma
|
||||
public void RecreateHead(ImmutableHashSet<Identifier> tags, int hairIndex, int beardIndex, int moustacheIndex, int faceAttachmentIndex)
|
||||
{
|
||||
HeadPreset headPreset = Prefab.Heads.FirstOrDefault(h => h.TagSet.SetEquals(tags));
|
||||
if (headPreset == null) { headPreset = Prefab.Heads.GetRandomUnsynced(); }
|
||||
if (headPreset == null)
|
||||
{
|
||||
if (tags.Count == 1)
|
||||
{
|
||||
headPreset = Prefab.Heads.FirstOrDefault(h => h.TagSet.Contains(tags.First()));
|
||||
}
|
||||
headPreset ??= Prefab.Heads.GetRandomUnsynced();
|
||||
}
|
||||
head = new HeadInfo(this, headPreset, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||
ReloadHeadAttachments();
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ namespace Barotrauma
|
||||
|
||||
public Vector2 Position
|
||||
{
|
||||
get { return ConvertUnits.ToDisplayUnits(body.SimPosition); }
|
||||
get { return ConvertUnits.ToDisplayUnits(body?.SimPosition ?? Vector2.Zero); }
|
||||
}
|
||||
|
||||
public Vector2 SimPosition
|
||||
|
||||
@@ -574,7 +574,7 @@ namespace Barotrauma
|
||||
public float AggressionGreed { get; private set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "If the health drops below this threshold, the character flees. In percentages."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
public float FleeHealthThreshold { get; private set; }
|
||||
public float FleeHealthThreshold { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Does the character attack when provoked? When enabled, overrides the predefined targeting state with Attack and increases the priority of it."), Editable()]
|
||||
public bool AttackWhenProvoked { get; private set; }
|
||||
|
||||
@@ -1122,7 +1122,7 @@ namespace Barotrauma
|
||||
{
|
||||
var gamesession = new GameSession(
|
||||
SubmarineInfo.SavedSubmarines.GetRandomUnsynced(s => s.Type == SubmarineType.Player && !s.HasTag(SubmarineTag.HideInMenus)),
|
||||
GameModePreset.DevSandbox);
|
||||
GameModePreset.DevSandbox ?? GameModePreset.Sandbox);
|
||||
string seed = ToolBox.RandomSeed(16);
|
||||
gamesession.StartRound(seed);
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@ namespace Barotrauma
|
||||
public enum AbilityEffectType
|
||||
{
|
||||
Undefined,
|
||||
None,
|
||||
None,
|
||||
OnAttack,
|
||||
OnAttackResult,
|
||||
OnAttacked,
|
||||
OnAttackedResult,
|
||||
OnGainSkillPoint,
|
||||
OnAllyGainSkillPoint,
|
||||
OnGainSkillPoint,
|
||||
OnAllyGainSkillPoint,
|
||||
OnRepairComplete,
|
||||
OnItemFabricationSkillGain,
|
||||
OnItemFabricatedAmount,
|
||||
@@ -155,4 +155,10 @@ namespace Barotrauma
|
||||
Player = 0b10,
|
||||
Both = Bot | Player
|
||||
}
|
||||
}
|
||||
|
||||
public enum NumberType
|
||||
{
|
||||
Int,
|
||||
Float
|
||||
}
|
||||
}
|
||||
@@ -313,10 +313,14 @@ namespace Barotrauma
|
||||
bool isValid = e is Character character && !character.Removed && !character.IsDead && !character.IsIncapacitated &&
|
||||
(e == Character.Controlled || character.IsRemotePlayer);
|
||||
#if SERVER
|
||||
UpdateIgnoredClients();
|
||||
isValid &= !ignoredClients.Keys.Any(c => c.Character == e);
|
||||
if (!dialogOpened)
|
||||
{
|
||||
UpdateIgnoredClients();
|
||||
isValid &= !ignoredClients.Keys.Any(c => c.Character == e);
|
||||
}
|
||||
#elif CLIENT
|
||||
isValid &= (e != Character.Controlled || !GUI.InputBlockingMenuOpen);
|
||||
bool block = GUI.InputBlockingMenuOpen && !dialogOpened;
|
||||
isValid &= (e != Character.Controlled || !block);
|
||||
#endif
|
||||
return isValid;
|
||||
}
|
||||
|
||||
@@ -135,8 +135,10 @@ namespace Barotrauma
|
||||
monster.Enabled = false;
|
||||
if (monster.Params.AI != null && monster.Params.AI.EnforceAggressiveBehaviorForMissions)
|
||||
{
|
||||
monster.Params.AI.FleeHealthThreshold = 0;
|
||||
foreach (var targetParam in monster.Params.AI.Targets)
|
||||
{
|
||||
if (targetParam.Tag.Equals("engine", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
switch (targetParam.State)
|
||||
{
|
||||
case AIState.Avoid:
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Barotrauma
|
||||
private readonly Dictionary<Identifier, List<Entity>> cachedTargets = new Dictionary<Identifier, List<Entity>>();
|
||||
private int prevEntityCount;
|
||||
private int prevPlayerCount, prevBotCount;
|
||||
private Character prevControlled;
|
||||
|
||||
private readonly string[] requiredDestinationTypes;
|
||||
public readonly bool RequireBeaconStation;
|
||||
@@ -163,12 +164,13 @@ namespace Barotrauma
|
||||
botCount++;
|
||||
}
|
||||
}
|
||||
if (Entity.EntityCount != prevEntityCount || botCount != prevBotCount || playerCount != prevPlayerCount)
|
||||
if (Entity.EntityCount != prevEntityCount || botCount != prevBotCount || playerCount != prevPlayerCount || prevControlled != Character.Controlled)
|
||||
{
|
||||
cachedTargets.Clear();
|
||||
prevEntityCount = Entity.EntityCount;
|
||||
prevBotCount = botCount;
|
||||
prevPlayerCount = playerCount;
|
||||
prevControlled = Character.Controlled;
|
||||
}
|
||||
|
||||
if (!Actions.Any())
|
||||
|
||||
@@ -339,11 +339,6 @@ namespace Barotrauma
|
||||
loadContext = null;
|
||||
assembly = null;
|
||||
}
|
||||
|
||||
~Implementation()
|
||||
{
|
||||
OnQuit();
|
||||
}
|
||||
}
|
||||
private static Implementation? loadedImplementation;
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -15,29 +14,29 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.NetworkMember != null && !GameMain.NetworkMember.IsServer) { return; }
|
||||
|
||||
bool skipMainSubs = GameMain.GameSession.GameMode is CampaignMode { IsFirstRound: false };
|
||||
if (!skipMainSubs)
|
||||
//player has more than one sub = we must have given the start items already
|
||||
bool startItemsGiven = GameMain.GameSession?.OwnedSubmarines != null && GameMain.GameSession.OwnedSubmarines.Count > 1;
|
||||
if (!startItemsGiven)
|
||||
{
|
||||
if (Submarine.MainSub is Submarine mainSub && mainSub.Info.IsPlayer)
|
||||
{
|
||||
SpawnStartItems(mainSub);
|
||||
}
|
||||
for (int i = 0; i < Submarine.MainSubs.Length; i++)
|
||||
{
|
||||
var sub = Submarine.MainSubs[i];
|
||||
if (sub == null || sub.Info.InitialSuppliesSpawned) { continue; }
|
||||
if (sub == null || sub.Info.InitialSuppliesSpawned || !sub.Info.IsPlayer) { continue; }
|
||||
//1st pass: items defined in the start item set, only spawned in the main sub (not drones/shuttles or other linked subs)
|
||||
SpawnStartItems(sub);
|
||||
//2nd pass: items defined using preferred containers, spawned in the main sub and all the linked subs (drones, shuttles etc)
|
||||
var subs = sub.GetConnectedSubs().Where(s => s.TeamID == sub.TeamID);
|
||||
CreateAndPlace(subs);
|
||||
subs.ForEach(s => s.Info.InitialSuppliesSpawned = true);
|
||||
}
|
||||
}
|
||||
|
||||
//spawn items in wrecks, beacon stations and pirate subs
|
||||
foreach (var sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub.Info.Type == SubmarineType.Player ||
|
||||
sub.Info.Type == SubmarineType.Outpost ||
|
||||
sub.Info.Type == SubmarineType.OutpostModule ||
|
||||
sub.Info.Type == SubmarineType.EnemySubmarine)
|
||||
sub.Info.Type == SubmarineType.OutpostModule)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -64,6 +63,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public static Identifier StartItemSet = new Identifier("normal");
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the items defined in the start item set in the specified sub.
|
||||
/// </summary>
|
||||
private static void SpawnStartItems(Submarine sub)
|
||||
{
|
||||
if (!Barotrauma.StartItemSet.Sets.TryGet(StartItemSet, out StartItemSet itemSet))
|
||||
|
||||
@@ -1081,15 +1081,10 @@ namespace Barotrauma
|
||||
{
|
||||
bool hasNewPendingSub = Campaign.PendingSubmarineSwitch != null &&
|
||||
Campaign.PendingSubmarineSwitch.MD5Hash.StringRepresentation != Submarine.Info.MD5Hash.StringRepresentation;
|
||||
|
||||
if (hasNewPendingSub)
|
||||
{
|
||||
Campaign.SwitchSubs();
|
||||
}
|
||||
else
|
||||
{
|
||||
SubmarineInfo = new SubmarineInfo(Submarine);
|
||||
}
|
||||
}
|
||||
rootElement.Add(new XAttribute("submarine", SubmarineInfo == null ? "" : SubmarineInfo.Name));
|
||||
if (OwnedSubmarines != null)
|
||||
|
||||
@@ -621,7 +621,7 @@ namespace Barotrauma.Items.Components
|
||||
hullRects[i].X -= expand;
|
||||
hullRects[i].Width += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]);
|
||||
hulls[i] = new Hull(hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
@@ -744,7 +744,7 @@ namespace Barotrauma.Items.Components
|
||||
hullRects[i].Y += expand;
|
||||
hullRects[i].Height += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]);
|
||||
hulls[i] = new Hull(hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
|
||||
@@ -3,7 +3,6 @@ using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get
|
||||
{
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body == null ? MathHelper.ToRadians(item.Rotation) : item.body.Rotation);
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body == null ? item.RotationRad : item.body.Rotation);
|
||||
Vector2 flippedPos = barrelPos;
|
||||
if (item.body != null && item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
|
||||
return Vector2.Transform(flippedPos, bodyTransform) * item.Scale;
|
||||
|
||||
@@ -17,11 +17,13 @@ namespace Barotrauma.Items.Components
|
||||
public readonly Item Item;
|
||||
public readonly StatusEffect StatusEffect;
|
||||
public readonly bool ExcludeBroken;
|
||||
public ActiveContainedItem(Item item, StatusEffect statusEffect, bool excludeBroken)
|
||||
public readonly bool ExcludeFullCondition;
|
||||
public ActiveContainedItem(Item item, StatusEffect statusEffect, bool excludeBroken, bool excludeFullCondition)
|
||||
{
|
||||
Item = item;
|
||||
StatusEffect = statusEffect;
|
||||
ExcludeBroken = excludeBroken;
|
||||
ExcludeFullCondition = excludeFullCondition;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +302,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!containableItem.MatchesItem(containedItem)) { continue; }
|
||||
foreach (StatusEffect effect in containableItem.statusEffects)
|
||||
{
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, containableItem.ExcludeBroken));
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, containableItem.ExcludeBroken, containableItem.ExcludeFullCondition));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,6 +410,7 @@ namespace Barotrauma.Items.Components
|
||||
Item contained = activeContainedItem.Item;
|
||||
|
||||
if (activeContainedItem.ExcludeBroken && contained.Condition <= 0.0f) { continue; }
|
||||
if (activeContainedItem.ExcludeFullCondition && contained.IsFullCondition) { continue; }
|
||||
StatusEffect effect = activeContainedItem.StatusEffect;
|
||||
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
||||
@@ -569,7 +572,7 @@ namespace Barotrauma.Items.Components
|
||||
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
|
||||
if (Math.Abs(item.Rotation) > 0.01f)
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
|
||||
Matrix transform = Matrix.CreateRotationZ(-item.RotationRad);
|
||||
transformedItemPos = Vector2.Transform(transformedItemPos - item.Position, transform) + item.Position;
|
||||
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
||||
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
|
||||
@@ -600,7 +603,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRotation += MathHelper.ToRadians(-item.Rotation);
|
||||
currentRotation += -item.RotationRad;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace Barotrauma.Items.Components
|
||||
hullData.ReceivedWaterAmount = null;
|
||||
if (fromWaterDetector)
|
||||
{
|
||||
hullData.ReceivedWaterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
hullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(sourceHull);
|
||||
}
|
||||
foreach (var linked in sourceHull.linkedTo)
|
||||
{
|
||||
@@ -174,7 +174,7 @@ namespace Barotrauma.Items.Components
|
||||
linkedHullData.ReceivedWaterAmount = null;
|
||||
if (fromWaterDetector)
|
||||
{
|
||||
linkedHullData.ReceivedWaterAmount = Math.Min(linkedHull.WaterVolume / linkedHull.Volume, 1.0f);
|
||||
linkedHullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(linkedHull);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -68,6 +68,8 @@ namespace Barotrauma.Items.Components
|
||||
private const float ConnectedSubUpdateInterval = 1.0f;
|
||||
float connectedSubUpdateTimer;
|
||||
|
||||
private double lastReceivedSteeringSignalTime;
|
||||
|
||||
public bool AutoPilot
|
||||
{
|
||||
get { return autoPilot; }
|
||||
@@ -312,16 +314,20 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (AutoPilot)
|
||||
{
|
||||
UpdateAutoPilot(deltaTime);
|
||||
float throttle = 1.0f;
|
||||
if (controlledSub != null)
|
||||
//signals override autopilot for a duration of one second
|
||||
if (lastReceivedSteeringSignalTime < Timing.TotalTime - 1)
|
||||
{
|
||||
//if the sub is heading in the correct direction, throttle the speed according to the user's skill
|
||||
//if it's e.g. sinking due to extra water, don't throttle, but allow emptying up the ballast completely
|
||||
throttle = MathHelper.Clamp(Vector2.Dot(controlledSub.Velocity, TargetVelocity) / 100.0f, 0.0f, 1.0f);
|
||||
UpdateAutoPilot(deltaTime);
|
||||
float throttle = 1.0f;
|
||||
if (controlledSub != null)
|
||||
{
|
||||
//if the sub is heading in the correct direction, throttle the speed according to the user's skill
|
||||
//if it's e.g. sinking due to extra water, don't throttle, but allow emptying up the ballast completely
|
||||
throttle = MathHelper.Clamp(Vector2.Dot(controlledSub.Velocity, TargetVelocity) / 100.0f, 0.0f, 1.0f);
|
||||
}
|
||||
float maxSpeed = MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f;
|
||||
TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(100.0f, maxSpeed, throttle));
|
||||
}
|
||||
float maxSpeed = MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f;
|
||||
TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(100.0f, maxSpeed, throttle));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -821,6 +827,7 @@ namespace Barotrauma.Items.Components
|
||||
steeringInput.X = MathHelper.Clamp(steeringInput.X, -100.0f, 100.0f);
|
||||
steeringInput.Y = MathHelper.Clamp(-steeringInput.Y, -100.0f, 100.0f);
|
||||
TargetVelocity = steeringInput;
|
||||
lastReceivedSteeringSignalTime = Timing.TotalTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (powerOut?.Grid != null) { return powerOut.Grid.Voltage; }
|
||||
}
|
||||
return currPowerConsumption <= 0.0f ? 1.0f : voltage;
|
||||
return PowerConsumption <= 0.0f ? 1.0f : voltage;
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -158,21 +158,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool PoweredByTinkering
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this is PowerContainer) { return false; }
|
||||
foreach (Repairable repairable in Item.Repairables)
|
||||
{
|
||||
if (repairable.IsTinkering && repairable.TinkeringPowersDevices)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool PoweredByTinkering { get; set; }
|
||||
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the item be damaged by electomagnetic pulses.")]
|
||||
public bool VulnerableToEMP
|
||||
|
||||
@@ -965,7 +965,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.body.LinearVelocity *= deflectedSpeedMultiplier;
|
||||
}
|
||||
else if ( stickJoint == null && StickTarget == null &&
|
||||
else if ( remainingHits <= 0 &&
|
||||
stickJoint == null && StickTarget == null &&
|
||||
StickToStructures && target.Body.UserData is Structure ||
|
||||
((StickToLightTargets || target.Body.Mass > item.body.Mass * 0.5f) &&
|
||||
(DoesStick ||
|
||||
|
||||
@@ -56,7 +56,8 @@ namespace Barotrauma.Items.Components
|
||||
if (value == qualityLevel) { return; }
|
||||
|
||||
bool wasInFullCondition = item.IsFullCondition;
|
||||
qualityLevel = MathHelper.Clamp(value, 0, MaxQuality);
|
||||
qualityLevel = MathHelper.Clamp(value, 0, MaxQuality);
|
||||
item.RecalculateConditionValues();
|
||||
//set the condition to the new max condition
|
||||
if (wasInFullCondition && statValues.ContainsKey(StatType.Condition))
|
||||
{
|
||||
|
||||
@@ -106,7 +106,25 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTinkering { get; private set; } = false;
|
||||
private bool isTinkering;
|
||||
public bool IsTinkering
|
||||
{
|
||||
get { return isTinkering; }
|
||||
private set
|
||||
{
|
||||
if (isTinkering == value) { return; }
|
||||
isTinkering = value;
|
||||
|
||||
if (tinkeringPowersDevices)
|
||||
{
|
||||
foreach (Powered powered in item.GetComponents<Powered>())
|
||||
{
|
||||
if (powered is PowerContainer) { continue; }
|
||||
powered.PoweredByTinkering = isTinkering;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Character CurrentFixer { get; private set; }
|
||||
private Item currentRepairItem;
|
||||
|
||||
@@ -160,7 +160,8 @@ namespace Barotrauma.Items.Components
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (source == null || target == null || target.Removed ||
|
||||
(source is Entity sourceEntity && sourceEntity.Removed))
|
||||
(source is Entity sourceEntity && sourceEntity.Removed) ||
|
||||
(source is Limb limb && limb.Removed))
|
||||
{
|
||||
ResetSource();
|
||||
target = null;
|
||||
|
||||
+60
-8
@@ -3,6 +3,7 @@ using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -34,13 +35,17 @@ namespace Barotrauma.Items.Components
|
||||
public Identifier PropertyName { get; }
|
||||
public bool TargetOnlyParentProperty { get; }
|
||||
|
||||
public int NumberInputMin { get; }
|
||||
public int NumberInputMax { get; }
|
||||
public string NumberInputMin { get; }
|
||||
public string NumberInputMax { get; }
|
||||
public string NumberInputStep { get; }
|
||||
public int NumberInputDecimalPlaces { get; }
|
||||
|
||||
public int MaxTextLength { get; }
|
||||
|
||||
public const int DefaultNumberInputMin = 0, DefaultNumberInputMax = 99;
|
||||
public bool IsIntegerInput { get; }
|
||||
public const string DefaultNumberInputMin = "0", DefaultNumberInputMax = "99", DefaultNumberInputStep = "1";
|
||||
public const int DefaultNumberInputDecimalPlaces = 0;
|
||||
public bool IsNumberInput { get; }
|
||||
public NumberType? NumberType { get; }
|
||||
public bool HasPropertyName { get; }
|
||||
public bool ShouldSetProperty { get; set; }
|
||||
|
||||
@@ -60,11 +65,34 @@ namespace Barotrauma.Items.Components
|
||||
ConnectionName = element.GetAttributeString("connection", "");
|
||||
PropertyName = element.GetAttributeIdentifier("propertyname", "");
|
||||
TargetOnlyParentProperty = element.GetAttributeBool("targetonlyparentproperty", false);
|
||||
NumberInputMin = element.GetAttributeInt("min", DefaultNumberInputMin);
|
||||
NumberInputMax = element.GetAttributeInt("max", DefaultNumberInputMax);
|
||||
NumberInputMin = element.GetAttributeString("min", DefaultNumberInputMin);
|
||||
NumberInputMax = element.GetAttributeString("max", DefaultNumberInputMax);
|
||||
NumberInputStep = element.GetAttributeString("step", DefaultNumberInputStep);
|
||||
NumberInputDecimalPlaces = element.GetAttributeInt("decimalplaces", DefaultNumberInputDecimalPlaces);
|
||||
MaxTextLength = element.GetAttributeInt("maxtextlength", int.MaxValue);
|
||||
|
||||
HasPropertyName = !PropertyName.IsEmpty;
|
||||
IsIntegerInput = HasPropertyName && element.Name.ToString().ToLowerInvariant() == "integerinput";
|
||||
if (HasPropertyName)
|
||||
{
|
||||
string elementName = element.Name.ToString().ToLowerInvariant();
|
||||
IsNumberInput = elementName == "numberinput" || elementName == "integerinput"; // backwards compatibility
|
||||
if (IsNumberInput)
|
||||
{
|
||||
string numberType = element.GetAttributeString("numbertype", string.Empty);
|
||||
switch (numberType)
|
||||
{
|
||||
case "f":
|
||||
case "float":
|
||||
NumberType = Barotrauma.NumberType.Float;
|
||||
break;
|
||||
case "int":
|
||||
case "integer":
|
||||
default: // backwards compatibility
|
||||
NumberType = Barotrauma.NumberType.Int;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element.GetAttribute("signal") is XAttribute attribute)
|
||||
{
|
||||
@@ -152,7 +180,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
case "button":
|
||||
case "textbox":
|
||||
case "integerinput":
|
||||
case "integerinput": // backwards compatibility
|
||||
case "numberinput":
|
||||
var button = new CustomInterfaceElement(item, subElement, this)
|
||||
{
|
||||
ContinuousSignal = false
|
||||
@@ -317,6 +346,24 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void ValueChanged(CustomInterfaceElement numberInputElement, float value)
|
||||
{
|
||||
if (numberInputElement == null) { return; }
|
||||
numberInputElement.Signal = value.ToString();
|
||||
if (!numberInputElement.TargetOnlyParentProperty)
|
||||
{
|
||||
foreach (ISerializableEntity e in item.AllPropertyObjects)
|
||||
{
|
||||
if (!e.SerializableProperties.ContainsKey(numberInputElement.PropertyName)) { continue; }
|
||||
e.SerializableProperties[numberInputElement.PropertyName].TrySetValue(e, value);
|
||||
}
|
||||
}
|
||||
else if (SerializableProperties.ContainsKey(numberInputElement.PropertyName))
|
||||
{
|
||||
SerializableProperties[numberInputElement.PropertyName].TrySetValue(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
foreach (CustomInterfaceElement ciElement in customInterfaceElementList)
|
||||
@@ -341,5 +388,10 @@ namespace Barotrauma.Items.Components
|
||||
signals = customInterfaceElementList.Select(ci => ci.Signal).ToArray();
|
||||
return base.Save(parentElement);
|
||||
}
|
||||
|
||||
private static bool TryParseFloatInvariantCulture(string s, out float f)
|
||||
{
|
||||
return float.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public static int GetWaterPercentage(Hull hull)
|
||||
{
|
||||
return hull.WaterVolume > 1.0f ? MathHelper.Clamp((int)Math.Ceiling(hull.WaterPercentage), 0, 100) : 0;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (stateSwitchDelay > 0.0f)
|
||||
@@ -103,12 +108,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
int waterPercentage = 0;
|
||||
//ignore minuscule amounts of water
|
||||
if (item.CurrentHull.WaterVolume > 1.0f)
|
||||
{
|
||||
waterPercentage = MathHelper.Clamp((int)Math.Ceiling(item.CurrentHull.WaterPercentage), 0, 100);
|
||||
}
|
||||
int waterPercentage = GetWaterPercentage(item.CurrentHull);
|
||||
if (prevSentWaterPercentageValue != waterPercentage || waterPercentageSignal == null)
|
||||
{
|
||||
prevSentWaterPercentageValue = waterPercentage;
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Barotrauma.Items.Components
|
||||
if (lightComponent != null)
|
||||
{
|
||||
lightComponent.Parent = null;
|
||||
lightComponent.Rotation = Rotation - MathHelper.ToRadians(item.Rotation);
|
||||
lightComponent.Rotation = Rotation - item.RotationRad;
|
||||
lightComponent.Light.Rotation = -rotation;
|
||||
}
|
||||
#endif
|
||||
@@ -516,7 +516,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (lightComponent != null)
|
||||
{
|
||||
lightComponent.Rotation = Rotation - MathHelper.ToRadians(item.Rotation);
|
||||
lightComponent.Rotation = Rotation - item.RotationRad;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -262,19 +262,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private float rotationRad;
|
||||
public float RotationRad { get; private set; }
|
||||
|
||||
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowRotating, MinValueFloat = 0.0f, MaxValueFloat = 360.0f, DecimalCount = 1, ValueStep = 1f), Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
public float Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
return MathHelper.ToDegrees(rotationRad);
|
||||
return MathHelper.ToDegrees(RotationRad);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!Prefab.AllowRotatingInEditor) { return; }
|
||||
rotationRad = MathHelper.ToRadians(value);
|
||||
RotationRad = MathHelper.ToRadians(value);
|
||||
#if CLIENT
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
@@ -472,9 +472,9 @@ namespace Barotrauma
|
||||
get { return spriteColor; }
|
||||
}
|
||||
|
||||
public bool IsFullCondition => MathUtils.NearlyEqual(Condition, MaxCondition);
|
||||
public float MaxCondition => Prefab.Health * healthMultiplier * maxRepairConditionMultiplier * (1.0f + GetQualityModifier(Items.Components.Quality.StatType.Condition));
|
||||
public float ConditionPercentage => MathUtils.Percentage(Condition, MaxCondition);
|
||||
public bool IsFullCondition { get; private set; }
|
||||
public float MaxCondition { get; private set; }
|
||||
public float ConditionPercentage { get; private set; }
|
||||
|
||||
private float offsetOnSelectedMultiplier = 1.0f;
|
||||
|
||||
@@ -495,7 +495,8 @@ namespace Barotrauma
|
||||
{
|
||||
float prevConditionPercentage = ConditionPercentage;
|
||||
healthMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity);
|
||||
Condition = MaxCondition * prevConditionPercentage / 100.0f;
|
||||
condition = MaxCondition * prevConditionPercentage / 100.0f;
|
||||
RecalculateConditionValues();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,7 +506,11 @@ namespace Barotrauma
|
||||
public float MaxRepairConditionMultiplier
|
||||
{
|
||||
get => maxRepairConditionMultiplier;
|
||||
set { maxRepairConditionMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity); }
|
||||
set
|
||||
{
|
||||
maxRepairConditionMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity);
|
||||
RecalculateConditionValues();
|
||||
}
|
||||
}
|
||||
|
||||
//the default value should be Prefab.Health, but because we can't use it in the attribute,
|
||||
@@ -806,7 +811,9 @@ namespace Barotrauma
|
||||
defaultRect = newRect;
|
||||
rect = newRect;
|
||||
|
||||
condition = MaxCondition;
|
||||
condition = MaxCondition = Prefab.Health;
|
||||
ConditionPercentage = 100.0f;
|
||||
|
||||
lastSentCondition = condition;
|
||||
|
||||
AllowDeconstruct = itemPrefab.AllowDeconstruct;
|
||||
@@ -1002,6 +1009,7 @@ namespace Barotrauma
|
||||
|
||||
ApplyStatusEffects(ActionType.OnSpawn, 1.0f);
|
||||
Components.ForEach(c => c.ApplyStatusEffects(ActionType.OnSpawn, 1.0f));
|
||||
RecalculateConditionValues();
|
||||
}
|
||||
|
||||
partial void InitProjSpecific();
|
||||
@@ -1184,7 +1192,6 @@ namespace Barotrauma
|
||||
public void RemoveContained(Item contained)
|
||||
{
|
||||
ownInventory?.RemoveItem(contained);
|
||||
|
||||
contained.Container = null;
|
||||
}
|
||||
|
||||
@@ -1611,6 +1618,10 @@ namespace Barotrauma
|
||||
bool wasInFullCondition = IsFullCondition;
|
||||
|
||||
condition = MathHelper.Clamp(value, 0.0f, MaxCondition);
|
||||
if (MathUtils.NearlyEqual(prev, condition, epsilon: 0.000001f)) { return; }
|
||||
|
||||
RecalculateConditionValues();
|
||||
|
||||
if (condition == 0.0f && prev > 0.0f)
|
||||
{
|
||||
//Flag connections to be updated as device is broken
|
||||
@@ -1672,6 +1683,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the item's maximum condition, condition percentage and whether it's in full condition.
|
||||
/// You generally never need to call this manually - done automatically when any of the factors that affect the values change.
|
||||
/// </summary>
|
||||
public void RecalculateConditionValues()
|
||||
{
|
||||
MaxCondition = Prefab.Health * healthMultiplier * maxRepairConditionMultiplier * (1.0f + GetQualityModifier(Items.Components.Quality.StatType.Condition));
|
||||
IsFullCondition = MathUtils.NearlyEqual(Condition, MaxCondition);
|
||||
ConditionPercentage = MathUtils.Percentage(Condition, MaxCondition);
|
||||
}
|
||||
|
||||
private bool IsInWater()
|
||||
{
|
||||
if (CurrentHull == null) { return true; }
|
||||
@@ -1999,7 +2021,7 @@ namespace Barotrauma
|
||||
|
||||
if (Prefab.AllowRotatingInEditor)
|
||||
{
|
||||
rotationRad = MathUtils.WrapAngleTwoPi(-rotationRad);
|
||||
RotationRad = MathUtils.WrapAngleTwoPi(-RotationRad);
|
||||
}
|
||||
#if CLIENT
|
||||
if (Prefab.CanSpriteFlipX)
|
||||
@@ -3153,12 +3175,12 @@ namespace Barotrauma
|
||||
{
|
||||
Vector2 oldRelativeOrigin = (oldPrefab.SwappableItem.SwapOrigin - oldPrefab.Size / 2) * element.GetAttributeFloat(item.scale, "scale", "Scale");
|
||||
oldRelativeOrigin.Y = -oldRelativeOrigin.Y;
|
||||
oldRelativeOrigin = MathUtils.RotatePoint(oldRelativeOrigin, -item.rotationRad);
|
||||
oldRelativeOrigin = MathUtils.RotatePoint(oldRelativeOrigin, -item.RotationRad);
|
||||
Vector2 oldOrigin = centerPos + oldRelativeOrigin;
|
||||
|
||||
Vector2 relativeOrigin = (prefab.SwappableItem.SwapOrigin - prefab.Size / 2) * item.Scale;
|
||||
relativeOrigin.Y = -relativeOrigin.Y;
|
||||
relativeOrigin = MathUtils.RotatePoint(relativeOrigin, -item.rotationRad);
|
||||
relativeOrigin = MathUtils.RotatePoint(relativeOrigin, -item.RotationRad);
|
||||
Vector2 origin = new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2) + relativeOrigin;
|
||||
|
||||
item.rect.Location -= (origin - oldOrigin).ToPoint();
|
||||
@@ -3194,6 +3216,7 @@ namespace Barotrauma
|
||||
item.condition = MathHelper.Clamp(condition, 0, item.MaxCondition);
|
||||
item.lastSentCondition = item.condition;
|
||||
|
||||
item.RecalculateConditionValues();
|
||||
item.SetActiveSprite();
|
||||
|
||||
if (submarine?.Info.GameVersion != null)
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool ExcludeBroken { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should full condition (100%) items be excluded
|
||||
/// </summary>
|
||||
public bool ExcludeFullCondition { get; private set; }
|
||||
|
||||
public bool AllowVariants { get; private set; } = true;
|
||||
|
||||
public RelationType Type
|
||||
@@ -102,14 +107,14 @@ namespace Barotrauma
|
||||
return CheckContained(parentItem);
|
||||
case RelationType.Container:
|
||||
if (parentItem == null || parentItem.Container == null) { return MatchOnEmpty; }
|
||||
return (!ExcludeBroken || parentItem.Container.Condition > 0.0f) && MatchesItem(parentItem.Container);
|
||||
return (!ExcludeBroken || parentItem.Container.Condition > 0.0f) && (!ExcludeFullCondition || !parentItem.Container.IsFullCondition) && MatchesItem(parentItem.Container);
|
||||
case RelationType.Equipped:
|
||||
if (character == null) { return false; }
|
||||
if (MatchOnEmpty && !character.HeldItems.Any()) { return true; }
|
||||
foreach (Item equippedItem in character.HeldItems)
|
||||
{
|
||||
if (equippedItem == null) { continue; }
|
||||
if ((!ExcludeBroken || equippedItem.Condition > 0.0f) && MatchesItem(equippedItem)) { return true; }
|
||||
if ((!ExcludeBroken || equippedItem.Condition > 0.0f) && (!ExcludeFullCondition || !equippedItem.IsFullCondition) && MatchesItem(equippedItem)) { return true; }
|
||||
}
|
||||
break;
|
||||
case RelationType.Picked:
|
||||
@@ -138,8 +143,7 @@ namespace Barotrauma
|
||||
foreach (Item contained in parentItem.ContainedItems)
|
||||
{
|
||||
if (TargetSlot > -1 && parentItem.OwnInventory.FindIndex(contained) != TargetSlot) { continue; }
|
||||
if ((!ExcludeBroken || contained.Condition > 0.0f) && MatchesItem(contained)) { return true; }
|
||||
|
||||
if ((!ExcludeBroken || contained.Condition > 0.0f) && (!ExcludeFullCondition || !contained.IsFullCondition) && MatchesItem(contained)) { return true; }
|
||||
if (CheckContained(contained)) { return true; }
|
||||
}
|
||||
return false;
|
||||
@@ -153,6 +157,7 @@ namespace Barotrauma
|
||||
new XAttribute("optional", IsOptional),
|
||||
new XAttribute("ignoreineditor", IgnoreInEditor),
|
||||
new XAttribute("excludebroken", ExcludeBroken),
|
||||
new XAttribute("excludefullcondition", ExcludeFullCondition),
|
||||
new XAttribute("targetslot", TargetSlot),
|
||||
new XAttribute("allowvariants", AllowVariants));
|
||||
|
||||
@@ -212,12 +217,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (identifiers.Length == 0 && excludedIdentifiers.Length == 0 && !returnEmpty) { return null; }
|
||||
|
||||
RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers)
|
||||
{
|
||||
ExcludeBroken = element.GetAttributeBool("excludebroken", true),
|
||||
ExcludeFullCondition = element.GetAttributeBool("excludefullcondition", false),
|
||||
AllowVariants = element.GetAttributeBool("allowvariants", true)
|
||||
};
|
||||
string typeStr = element.GetAttributeString("type", "");
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#nullable enable
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal class StartItem
|
||||
{
|
||||
public Identifier Item;
|
||||
public int Amount;
|
||||
|
||||
public StartItem(XElement element)
|
||||
{
|
||||
Item = element.GetAttributeIdentifier("identifier", Identifier.Empty);
|
||||
Amount = element.GetAttributeInt("amount", 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additive sets of items spawned only at the start of the game.
|
||||
/// </summary>
|
||||
internal class StartItemSet : PrefabWithUintIdentifier
|
||||
{
|
||||
public readonly static PrefabCollection<StartItemSet> Sets = new PrefabCollection<StartItemSet>();
|
||||
|
||||
public readonly ImmutableArray<StartItem> Items;
|
||||
|
||||
public StartItemSet(ContentXElement element, StartItemsFile file) : base(file, element.GetAttributeIdentifier("identifier", Identifier.Empty))
|
||||
{
|
||||
Items = element.Elements().Select(e => new StartItem(e!)).ToImmutableArray();
|
||||
}
|
||||
|
||||
public override void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ namespace Barotrauma
|
||||
IEnumerable<string> aliases = null)
|
||||
: base(identifier)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(constructor != null);
|
||||
this.constructor = constructor;
|
||||
this.Name = TextManager.Get($"EntityName.{identifier}");
|
||||
this.Description = TextManager.Get($"EntityDescription.{identifier}");
|
||||
@@ -35,40 +36,52 @@ namespace Barotrauma
|
||||
this.Aliases = (aliases ?? Enumerable.Empty<string>()).Concat(identifier.Value.ToEnumerable()).ToImmutableHashSet();
|
||||
}
|
||||
|
||||
public static CoreEntityPrefab HullPrefab { get; private set; }
|
||||
public static CoreEntityPrefab GapPrefab { get; private set; }
|
||||
public static CoreEntityPrefab WayPointPrefab { get; private set; }
|
||||
public static CoreEntityPrefab SpawnPointPrefab { get; private set; }
|
||||
|
||||
public static void InitCorePrefabs()
|
||||
{
|
||||
CoreEntityPrefab ep = new CoreEntityPrefab(
|
||||
HullPrefab = new CoreEntityPrefab(
|
||||
"hull".ToIdentifier(),
|
||||
typeof(Hull).GetConstructor(new Type[] { typeof(MapEntityPrefab), typeof(Rectangle) }),
|
||||
typeof(Hull).GetConstructor(new Type[] { typeof(Rectangle) }),
|
||||
resizeHorizontal: true,
|
||||
resizeVertical: true,
|
||||
linkable: true,
|
||||
allowedLinks: new Identifier[] { "hull".ToIdentifier() });
|
||||
Prefabs.Add(ep, false);
|
||||
Prefabs.Add(HullPrefab, false);
|
||||
|
||||
ep = new CoreEntityPrefab(
|
||||
GapPrefab = new CoreEntityPrefab(
|
||||
"gap".ToIdentifier(),
|
||||
typeof(Gap).GetConstructor(new Type[] { typeof(MapEntityPrefab), typeof(Rectangle) }),
|
||||
typeof(Gap).GetConstructor(new Type[] { typeof(Rectangle) }),
|
||||
resizeHorizontal: true,
|
||||
resizeVertical: true);
|
||||
Prefabs.Add(ep, false);
|
||||
Prefabs.Add(GapPrefab, false);
|
||||
|
||||
ep = new CoreEntityPrefab(
|
||||
WayPointPrefab = new CoreEntityPrefab(
|
||||
"waypoint".ToIdentifier(),
|
||||
typeof(WayPoint).GetConstructor(new Type[] { typeof(MapEntityPrefab), typeof(Rectangle) }));
|
||||
Prefabs.Add(ep, false);
|
||||
Prefabs.Add(WayPointPrefab, false);
|
||||
|
||||
ep = new CoreEntityPrefab(
|
||||
SpawnPointPrefab = new CoreEntityPrefab(
|
||||
"spawnpoint".ToIdentifier(),
|
||||
typeof(WayPoint).GetConstructor(new Type[] { typeof(MapEntityPrefab), typeof(Rectangle) }));
|
||||
Prefabs.Add(ep, false);
|
||||
Prefabs.Add(SpawnPointPrefab, false);
|
||||
}
|
||||
|
||||
protected override void CreateInstance(Rectangle rect)
|
||||
{
|
||||
if (constructor == null) return;
|
||||
object[] lobject = new object[] { this, rect };
|
||||
constructor.Invoke(lobject);
|
||||
if (this == WayPointPrefab || this == SpawnPointPrefab)
|
||||
{
|
||||
object[] lobject = new object[] { this, rect };
|
||||
constructor.Invoke(lobject);
|
||||
}
|
||||
else
|
||||
{
|
||||
object[] lobject = new object[] { rect };
|
||||
constructor.Invoke(lobject);
|
||||
}
|
||||
}
|
||||
|
||||
private bool disposed = false;
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Gap(MapEntityPrefab prefab, Rectangle rectangle)
|
||||
public Gap(Rectangle rectangle)
|
||||
: this(rectangle, Submarine.MainSub)
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -136,7 +136,7 @@ namespace Barotrauma
|
||||
{ }
|
||||
|
||||
public Gap(Rectangle rect, bool isHorizontal, Submarine submarine, ushort id = Entity.NullEntityID)
|
||||
: base(MapEntityPrefab.FindByIdentifier("gap".ToIdentifier()), submarine, id)
|
||||
: base(CoreEntityPrefab.GapPrefab, submarine, id)
|
||||
{
|
||||
this.rect = rect;
|
||||
flowForce = Vector2.Zero;
|
||||
|
||||
@@ -410,8 +410,8 @@ namespace Barotrauma
|
||||
|
||||
public BallastFloraBehavior BallastFlora { get; set; }
|
||||
|
||||
public Hull(MapEntityPrefab prefab, Rectangle rectangle)
|
||||
: this (prefab, rectangle, Submarine.MainSub)
|
||||
public Hull(Rectangle rectangle)
|
||||
: this (rectangle, Submarine.MainSub)
|
||||
{
|
||||
#if CLIENT
|
||||
if (SubEditorScreen.IsSubEditor())
|
||||
@@ -421,8 +421,8 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public Hull(MapEntityPrefab prefab, Rectangle rectangle, Submarine submarine, ushort id = Entity.NullEntityID)
|
||||
: base (prefab, submarine, id)
|
||||
public Hull(Rectangle rectangle, Submarine submarine, ushort id = Entity.NullEntityID)
|
||||
: base (CoreEntityPrefab.HullPrefab, submarine, id)
|
||||
{
|
||||
rect = rectangle;
|
||||
|
||||
@@ -500,7 +500,7 @@ namespace Barotrauma
|
||||
|
||||
public override MapEntity Clone()
|
||||
{
|
||||
var clone = new Hull(MapEntityPrefab.FindByIdentifier("hull".ToIdentifier()), rect, Submarine);
|
||||
var clone = new Hull(rect, Submarine);
|
||||
foreach (KeyValuePair<Identifier, SerializableProperty> property in SerializableProperties)
|
||||
{
|
||||
if (!property.Value.Attributes.OfType<Editable>().Any()) { continue; }
|
||||
@@ -1543,7 +1543,7 @@ namespace Barotrauma
|
||||
int.Parse(element.GetAttribute("height").Value));
|
||||
}
|
||||
|
||||
var hull = new Hull(MapEntityPrefab.Find(null, "hull"), rect, submarine, idRemap.GetOffsetId(element))
|
||||
var hull = new Hull(rect, submarine, idRemap.GetOffsetId(element))
|
||||
{
|
||||
WaterVolume = element.GetAttributeFloat("pressure", 0.0f)
|
||||
};
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -14,6 +11,8 @@ namespace Barotrauma
|
||||
public readonly LocalizedString Description;
|
||||
|
||||
public readonly bool IsEndBiome;
|
||||
public readonly float MinDifficulty;
|
||||
public readonly float MaxDifficulty;
|
||||
|
||||
public readonly ImmutableHashSet<int> AllowedZones;
|
||||
|
||||
@@ -30,8 +29,9 @@ namespace Barotrauma
|
||||
element.GetAttributeString("description", ""));
|
||||
|
||||
IsEndBiome = element.GetAttributeBool("endbiome", false);
|
||||
|
||||
AllowedZones = element.GetAttributeIntArray("AllowedZones", new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }).ToImmutableHashSet();
|
||||
MinDifficulty = element.GetAttributeFloat("MinDifficulty", 0);
|
||||
MaxDifficulty = element.GetAttributeFloat("MaxDifficulty", 100);
|
||||
}
|
||||
|
||||
public static Identifier ParseIdentifier(ContentXElement element)
|
||||
|
||||
@@ -96,24 +96,31 @@ namespace Barotrauma
|
||||
public readonly Sprite WallSprite;
|
||||
public readonly Sprite WallEdgeSprite;
|
||||
|
||||
public static CaveGenerationParams GetRandom(LevelGenerationParams generationParams, bool abyss, Rand.RandSync rand)
|
||||
public static CaveGenerationParams GetRandom(Level level, bool abyss, Rand.RandSync rand)
|
||||
{
|
||||
var caveParams = CaveParams.OrderBy(p => p.UintIdentifier).ToList();
|
||||
if (caveParams.All(p => p.GetCommonness(generationParams, abyss) <= 0.0f))
|
||||
if (caveParams.All(p => p.GetCommonness(level.LevelData, abyss) <= 0.0f))
|
||||
{
|
||||
return caveParams.First();
|
||||
}
|
||||
return ToolBox.SelectWeightedRandom(caveParams.ToList(), caveParams.Select(p => p.GetCommonness(generationParams, abyss)).ToList(), rand);
|
||||
return ToolBox.SelectWeightedRandom(caveParams.ToList(), caveParams.Select(p => p.GetCommonness(level.LevelData, abyss)).ToList(), rand);
|
||||
}
|
||||
|
||||
public float GetCommonness(LevelGenerationParams generationParams, bool abyss)
|
||||
public float GetCommonness(LevelData levelData, bool abyss)
|
||||
{
|
||||
if (generationParams != null &&
|
||||
generationParams.Identifier != Identifier.Empty &&
|
||||
OverrideCommonness.TryGetValue(abyss ? "abyss".ToIdentifier() : generationParams.Identifier, out float commonness))
|
||||
if (levelData.GenerationParams != null && levelData.GenerationParams.Identifier != Identifier.Empty &&
|
||||
OverrideCommonness.TryGetValue(abyss ? "abyss".ToIdentifier() : levelData.GenerationParams.Identifier, out float commonness))
|
||||
{
|
||||
return commonness;
|
||||
}
|
||||
if (levelData?.Biome != null)
|
||||
{
|
||||
if (OverrideCommonness.TryGetValue(levelData.Biome.Identifier, out float biomeCommonness))
|
||||
{
|
||||
return biomeCommonness;
|
||||
}
|
||||
}
|
||||
|
||||
return Commonness;
|
||||
}
|
||||
|
||||
|
||||
@@ -442,6 +442,11 @@ namespace Barotrauma
|
||||
Loaded?.Remove();
|
||||
Loaded = this;
|
||||
Generating = true;
|
||||
#if CLIENT
|
||||
Debug.Assert(GenerationParams.Identifier != "coldcavernstutorial" || GameMain.GameSession?.GameMode == null || GameMain.GameSession.GameMode is TutorialMode);
|
||||
#endif
|
||||
Debug.Assert(GenerationParams.AnyBiomeAllowed || GenerationParams.AllowedBiomeIdentifiers.Contains(LevelData.Biome.Identifier));
|
||||
DebugConsole.NewMessage("Level identifier: " + GenerationParams.Identifier);
|
||||
|
||||
ClearEqualityCheckValues();
|
||||
EntitiesBeforeGenerate = GetEntities().ToList();
|
||||
@@ -1711,7 +1716,8 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
//if the bottom of the abyss area is below crush depth, try to move it up to keep (most) of the abyss content above crush depth
|
||||
if (abyssEndY + CrushDepth < 0)
|
||||
//but only if start of the abyss is above crush depth (no point in doing this if all of it is below crush depth)
|
||||
if (abyssEndY + CrushDepth < 0 && abyssStartY > -CrushDepth)
|
||||
{
|
||||
abyssEndY += Math.Min(-(abyssEndY + (int)CrushDepth), abyssHeight / 2);
|
||||
}
|
||||
@@ -1820,7 +1826,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var caveParams = CaveGenerationParams.GetRandom(GenerationParams, abyss: true, rand: Rand.RandSync.ServerAndClient);
|
||||
var caveParams = CaveGenerationParams.GetRandom(this, abyss: true, rand: Rand.RandSync.ServerAndClient);
|
||||
|
||||
float caveScaleRelativeToIsland = 0.7f;
|
||||
GenerateCave(
|
||||
@@ -1889,7 +1895,7 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < GenerationParams.CaveCount; i++)
|
||||
{
|
||||
var caveParams = CaveGenerationParams.GetRandom(GenerationParams, abyss: false, rand: Rand.RandSync.ServerAndClient);
|
||||
var caveParams = CaveGenerationParams.GetRandom(this, abyss: false, rand: Rand.RandSync.ServerAndClient);
|
||||
Point caveSize = new Point(
|
||||
Rand.Range(caveParams.MinWidth, caveParams.MaxWidth, Rand.RandSync.ServerAndClient),
|
||||
Rand.Range(caveParams.MinHeight, caveParams.MaxHeight, Rand.RandSync.ServerAndClient));
|
||||
@@ -2479,6 +2485,7 @@ namespace Barotrauma
|
||||
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs.OrderBy(p => p.UintIdentifier))
|
||||
{
|
||||
if (itemPrefab.LevelCommonness.TryGetValue(levelName, out float commonness) ||
|
||||
itemPrefab.LevelCommonness.TryGetValue(LevelData.Biome.Identifier, out commonness) ||
|
||||
itemPrefab.LevelCommonness.TryGetValue(Identifier.Empty, out commonness))
|
||||
{
|
||||
if (commonness <= 0.0f) { continue; }
|
||||
@@ -3237,7 +3244,8 @@ namespace Barotrauma
|
||||
if (index < 0 || index >= bottomPositions.Count - 1) { return new Vector2(xPosition, BottomPos); }
|
||||
|
||||
float t = (xPosition - bottomPositions[index].X) / (bottomPositions[index + 1].X - bottomPositions[index].X);
|
||||
Debug.Assert(t <= 1.0f);
|
||||
//t can go slightly outside the 0-1 due to rounding, safe to ignore
|
||||
Debug.Assert(t <= 1.001f && t >= -0.001f);
|
||||
t = MathHelper.Clamp(t, 0.0f, 1.0f);
|
||||
|
||||
float yPos = MathHelper.Lerp(bottomPositions[index].Y, bottomPositions[index + 1].Y, t);
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma
|
||||
|
||||
public readonly string Seed;
|
||||
|
||||
public readonly float Difficulty;
|
||||
public float Difficulty;
|
||||
|
||||
public readonly Biome Biome;
|
||||
|
||||
@@ -90,10 +90,10 @@ namespace Barotrauma
|
||||
(int)MathUtils.Round(generationParams.Height, Level.GridCellSize));
|
||||
}
|
||||
|
||||
public LevelData(XElement element)
|
||||
public LevelData(XElement element, float? forceDifficulty = null)
|
||||
{
|
||||
Seed = element.GetAttributeString("seed", "");
|
||||
Difficulty = element.GetAttributeFloat("difficulty", 0.0f);
|
||||
Difficulty = forceDifficulty ?? element.GetAttributeFloat("difficulty", 0.0f);
|
||||
Size = element.GetAttributePoint("size", new Point(1000));
|
||||
Enum.TryParse(element.GetAttributeString("type", "LocationConnection"), out Type);
|
||||
|
||||
|
||||
@@ -414,7 +414,7 @@ namespace Barotrauma
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(50, IsPropertySaveable.Yes, description: "Maximum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
[Serialize(40, IsPropertySaveable.Yes, description: "Maximum number of resource clusters in the abyss (the actual number is picked between min and max according to the level difficulty)"), Editable(MinValueInt = 0, MaxValueInt = 1000)]
|
||||
public int AbyssResourceClustersMax
|
||||
{
|
||||
get;
|
||||
|
||||
+4
-4
@@ -170,7 +170,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
//get a random prefab and find a place to spawn it
|
||||
LevelObjectPrefab prefab = GetRandomPrefab(level.GenerationParams, availablePrefabs);
|
||||
LevelObjectPrefab prefab = GetRandomPrefab(level, availablePrefabs);
|
||||
if (prefab == null) { continue; }
|
||||
if (!suitableSpawnPositions.ContainsKey(prefab))
|
||||
{
|
||||
@@ -595,12 +595,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private LevelObjectPrefab GetRandomPrefab(LevelGenerationParams generationParams, IList<LevelObjectPrefab> availablePrefabs)
|
||||
private LevelObjectPrefab GetRandomPrefab(Level level, IList<LevelObjectPrefab> availablePrefabs)
|
||||
{
|
||||
if (availablePrefabs.Sum(p => p.GetCommonness(generationParams)) <= 0.0f) { return null; }
|
||||
if (availablePrefabs.Sum(p => p.GetCommonness(level.LevelData)) <= 0.0f) { return null; }
|
||||
return ToolBox.SelectWeightedRandom(
|
||||
availablePrefabs,
|
||||
availablePrefabs.Select(p => p.GetCommonness(generationParams)).ToList(), Rand.RandSync.ServerAndClient);
|
||||
availablePrefabs.Select(p => p.GetCommonness(level.LevelData)).ToList(), Rand.RandSync.ServerAndClient);
|
||||
}
|
||||
|
||||
private LevelObjectPrefab GetRandomPrefab(CaveGenerationParams caveParams, IList<LevelObjectPrefab> availablePrefabs, bool requireCaveSpecificOverride)
|
||||
|
||||
+12
-6
@@ -426,15 +426,21 @@ namespace Barotrauma
|
||||
return requireCaveSpecificOverride ? 0.0f : Commonness;
|
||||
}
|
||||
|
||||
public float GetCommonness(LevelGenerationParams generationParams)
|
||||
{
|
||||
if (generationParams != null &&
|
||||
generationParams.Identifier != Identifier.Empty &&
|
||||
(OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness) ||
|
||||
(!generationParams.OldIdentifier.IsEmpty && OverrideCommonness.TryGetValue(generationParams.OldIdentifier, out commonness))))
|
||||
public float GetCommonness(LevelData levelData)
|
||||
{
|
||||
if (levelData.GenerationParams != null && levelData.GenerationParams.Identifier != Identifier.Empty &&
|
||||
OverrideCommonness.TryGetValue(levelData.GenerationParams.Identifier, out float commonness) ||
|
||||
(!levelData.GenerationParams.OldIdentifier.IsEmpty && OverrideCommonness.TryGetValue(levelData.GenerationParams.OldIdentifier, out commonness)))
|
||||
{
|
||||
return commonness;
|
||||
}
|
||||
if (levelData?.Biome != null)
|
||||
{
|
||||
if (OverrideCommonness.TryGetValue(levelData.Biome.Identifier, out float biomeCommonness))
|
||||
{
|
||||
return biomeCommonness;
|
||||
}
|
||||
}
|
||||
return Commonness;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,12 +152,6 @@ namespace Barotrauma
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
#if CLIENT
|
||||
VertexBuffer?.Dispose();
|
||||
VertexBuffer = null;
|
||||
|
||||
@@ -485,6 +485,19 @@ namespace Barotrauma
|
||||
TurnsInRadiation = element.GetAttributeInt(nameof(TurnsInRadiation).ToLower(), 0);
|
||||
StepsSinceSpecialsUpdated = element.GetAttributeInt("stepssincespecialsupdated", 0);
|
||||
|
||||
Identifier biomeId = element.GetAttributeIdentifier("biome", Identifier.Empty);
|
||||
if (biomeId != Identifier.Empty)
|
||||
{
|
||||
if (Biome.Prefabs.TryGet(biomeId, out Biome biome))
|
||||
{
|
||||
Biome = biome;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error while loading the campaign map: could not find a biome with the identifier \"{biomeId}\".");
|
||||
}
|
||||
}
|
||||
|
||||
if (!typeNotFound)
|
||||
{
|
||||
for (int i = 0; i < Type.CanChangeTo.Count; i++)
|
||||
@@ -773,22 +786,41 @@ namespace Barotrauma
|
||||
|
||||
static float GetConnectionWeight(Location location, LocationConnection c)
|
||||
{
|
||||
float weight = c.Passed ? 1.0f : 5.0f;
|
||||
Location destination = c.OtherLocation(location);
|
||||
if (destination != null)
|
||||
if (destination == null) { return 0; }
|
||||
float minWeight = 0.0001f;
|
||||
float lowWeight = 0.2f;
|
||||
float normalWeight = 1.0f;
|
||||
float maxWeight = 2.0f;
|
||||
float weight = c.Passed ? lowWeight : normalWeight;
|
||||
if (location.Biome.AllowedZones.Contains(1))
|
||||
{
|
||||
if (destination.MapPosition.X > location.MapPosition.X) { weight *= 2.0f; }
|
||||
int missionCount = location.availableMissions.Count(m => m.Locations.Contains(destination));
|
||||
if (missionCount > 0)
|
||||
{
|
||||
weight /= missionCount * 2;
|
||||
}
|
||||
if (destination.IsRadiated())
|
||||
// In the first biome, give a stronger preference for locations that are farther to the right)
|
||||
float diff = destination.MapPosition.X - location.MapPosition.X;
|
||||
if (diff < 0)
|
||||
{
|
||||
weight *= 0.001f;
|
||||
weight *= 0.1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
float maxRelevantDiff = 300;
|
||||
weight = MathHelper.Lerp(weight, maxWeight, MathUtils.InverseLerp(0, maxRelevantDiff, diff));
|
||||
}
|
||||
}
|
||||
return weight;
|
||||
else if (destination.MapPosition.X > location.MapPosition.X)
|
||||
{
|
||||
weight *= 2.0f;
|
||||
}
|
||||
int missionCount = location.availableMissions.Count(m => m.Locations.Contains(destination));
|
||||
if (missionCount > 0)
|
||||
{
|
||||
weight /= missionCount * 2;
|
||||
}
|
||||
if (destination.IsRadiated())
|
||||
{
|
||||
weight *= 0.001f;
|
||||
}
|
||||
return MathHelper.Clamp(weight, minWeight, maxWeight);
|
||||
}
|
||||
|
||||
return InstantiateMission(prefab, connection);
|
||||
@@ -1255,6 +1287,7 @@ namespace Barotrauma
|
||||
new XAttribute("originaltype", (Type ?? OriginalType).Identifier),
|
||||
new XAttribute("basename", BaseName),
|
||||
new XAttribute("name", Name),
|
||||
new XAttribute("biome", Biome?.Identifier.Value ?? string.Empty),
|
||||
new XAttribute("discovered", Discovered),
|
||||
new XAttribute("position", XMLExtensions.Vector2ToString(MapPosition)),
|
||||
new XAttribute("pricemultiplier", PriceMultiplier),
|
||||
|
||||
@@ -131,18 +131,27 @@ namespace Barotrauma
|
||||
};
|
||||
Locations[locationIndices.X].Connections.Add(connection);
|
||||
Locations[locationIndices.Y].Connections.Add(connection);
|
||||
connection.LevelData = new LevelData(subElement.Element("Level"));
|
||||
string biomeId = subElement.GetAttributeString("biome", "");
|
||||
connection.Biome =
|
||||
Biome.Prefabs.FirstOrDefault(b => b.Identifier == biomeId) ??
|
||||
Biome.Prefabs.FirstOrDefault(b => !b.OldIdentifier.IsEmpty && b.OldIdentifier == biomeId) ??
|
||||
Biome.Prefabs.First();
|
||||
connection.Difficulty = MathHelper.Clamp(connection.Difficulty, connection.Biome.MinDifficulty, connection.Biome.MaxDifficulty);
|
||||
connection.LevelData = new LevelData(subElement.Element("Level"), connection.Difficulty);
|
||||
Connections.Add(connection);
|
||||
connectionElements.Add(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//backwards compatibility: location biomes weren't saved (or used for anything) previously,
|
||||
//assign them if they haven't been assigned
|
||||
Random rand = new MTRandom(ToolBox.StringToInt(Seed));
|
||||
if (Locations.First().Biome == null)
|
||||
{
|
||||
AssignBiomes(rand);
|
||||
}
|
||||
|
||||
int startLocationindex = element.GetAttributeInt("startlocation", -1);
|
||||
if (startLocationindex > 0 && startLocationindex < Locations.Count)
|
||||
{
|
||||
@@ -237,6 +246,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(StartLocation != null, "Start location not assigned after level generation.");
|
||||
if (StartLocation?.LevelData != null)
|
||||
{
|
||||
StartLocation.LevelData.Difficulty = 0;
|
||||
}
|
||||
|
||||
//ensure all paths from the starting location have 0 difficulty to make the 1st campaign round very easy
|
||||
foreach (var locationConnection in StartLocation.Connections)
|
||||
@@ -251,6 +264,11 @@ namespace Barotrauma
|
||||
CurrentLocation.Discover(true);
|
||||
CurrentLocation.CreateStores();
|
||||
|
||||
foreach (var location in Locations)
|
||||
{
|
||||
location.UnlockInitialMissions();
|
||||
}
|
||||
|
||||
InitProjectSpecific();
|
||||
}
|
||||
|
||||
@@ -505,22 +523,31 @@ namespace Barotrauma
|
||||
//remove orphans
|
||||
Locations.RemoveAll(l => !Connections.Any(c => c.Locations.Contains(l)));
|
||||
|
||||
AssignBiomes(new MTRandom(ToolBox.StringToInt(Seed)));
|
||||
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
//float difficulty = GetLevelDifficulty(connection.CenterPos.X / Width);
|
||||
//connection.Difficulty = MathHelper.Clamp(difficulty + Rand.Range(-10.0f, 0.0f, Rand.RandSync.ServerAndClient), 1.2f, 100.0f);
|
||||
float difficulty = connection.CenterPos.X / Width * 100;
|
||||
float random = difficulty > 10 ? 5 : 0;
|
||||
connection.Difficulty = MathHelper.Clamp(difficulty + Rand.Range(-random, random, Rand.RandSync.ServerAndClient), 1.0f, 100.0f);
|
||||
float minDifficulty = 0;
|
||||
float maxDifficulty = 100;
|
||||
var biome = connection.Biome;
|
||||
if (biome != null)
|
||||
{
|
||||
minDifficulty = connection.Biome.MinDifficulty;
|
||||
maxDifficulty = connection.Biome.MaxDifficulty;
|
||||
if (connection.Locked)
|
||||
{
|
||||
connection.Difficulty = maxDifficulty;
|
||||
}
|
||||
}
|
||||
connection.Difficulty = MathHelper.Clamp(difficulty, minDifficulty, maxDifficulty);
|
||||
}
|
||||
|
||||
AssignBiomes();
|
||||
CreateEndLocation();
|
||||
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
location.LevelData = new LevelData(location, MathHelper.Clamp(location.MapPosition.X / Width * 100, 0.0f, 100.0f));
|
||||
location.UnlockInitialMissions();
|
||||
}
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
@@ -549,7 +576,7 @@ namespace Barotrauma
|
||||
return Biome.Prefabs.FirstOrDefault(b => b.AllowedZones.Contains(zoneIndex));
|
||||
}
|
||||
|
||||
private void AssignBiomes()
|
||||
private void AssignBiomes(Random rand)
|
||||
{
|
||||
var biomes = Biome.Prefabs;
|
||||
float zoneWidth = Width / generationParams.DifficultyZones;
|
||||
@@ -565,7 +592,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (location.MapPosition.X < zoneX)
|
||||
{
|
||||
location.Biome = allowedBiomes[Rand.Range(0, allowedBiomes.Count, Rand.RandSync.ServerAndClient)];
|
||||
location.Biome = allowedBiomes[rand.Next() % allowedBiomes.Count];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,23 +34,6 @@ namespace Barotrauma
|
||||
return prefab;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!Disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Humans.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
public override void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -109,14 +109,21 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public enum Type
|
||||
{
|
||||
WayPoint,
|
||||
SpawnPoint
|
||||
}
|
||||
|
||||
public WayPoint(Rectangle newRect, Submarine submarine)
|
||||
: this (MapEntityPrefab.FindByIdentifier("waypoint".ToIdentifier()), newRect, submarine)
|
||||
: this (Type.WayPoint, newRect, submarine)
|
||||
{
|
||||
}
|
||||
|
||||
public WayPoint(MapEntityPrefab prefab, Rectangle newRect, Submarine submarine, ushort id = Entity.NullEntityID)
|
||||
: base (prefab, submarine, id)
|
||||
public WayPoint(Type type, Rectangle newRect, Submarine submarine, ushort id = Entity.NullEntityID)
|
||||
: base (type is Type.WayPoint
|
||||
? CoreEntityPrefab.WayPointPrefab
|
||||
: CoreEntityPrefab.SpawnPointPrefab, submarine, id)
|
||||
{
|
||||
rect = newRect;
|
||||
idCardTags = Array.Empty<string>();
|
||||
@@ -1010,7 +1017,7 @@ namespace Barotrauma
|
||||
|
||||
|
||||
Enum.TryParse(element.GetAttributeString("spawn", "Path"), out SpawnType spawnType);
|
||||
WayPoint w = new WayPoint(MapEntityPrefab.FindByIdentifier((spawnType == SpawnType.Path ? "waypoint" : "spawnpoint").ToIdentifier()), rect, submarine, idRemap.GetOffsetId(element))
|
||||
WayPoint w = new WayPoint(spawnType == SpawnType.Path ? Type.WayPoint : Type.SpawnPoint, rect, submarine, idRemap.GetOffsetId(element))
|
||||
{
|
||||
spawnType = spawnType
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Barotrauma.Networking
|
||||
return -1;
|
||||
}
|
||||
|
||||
//FIXME workaround for crash when closing the server under .NET 6.0, not sure if this is the proper way to fix it but it prevents it from crashing the client. - Markus
|
||||
// BUG workaround for crash when closing the server under .NET 6.0, not sure if this is the proper way to fix it but it prevents it from crashing the client. - Markus
|
||||
#if NET6_0
|
||||
try
|
||||
{
|
||||
|
||||
@@ -45,6 +45,12 @@ namespace Barotrauma
|
||||
bool matchingElementFound = false;
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (replacementSubElement.Name.ToString().Equals("clear", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
matchingElementFound = true;
|
||||
elementsToRemove.AddRange(element.Elements());
|
||||
break;
|
||||
}
|
||||
if (!subElement.Name.ToString().Equals(replacementSubElement.Name.ToString(), StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (i == index)
|
||||
{
|
||||
|
||||
@@ -161,6 +161,7 @@ namespace Barotrauma
|
||||
RadialDistortion = true,
|
||||
InventoryScale = 1.0f,
|
||||
LightMapScale = 1.0f,
|
||||
VisibleLightLimit = 50,
|
||||
TextScale = 1.0f,
|
||||
HUDScale = 1.0f,
|
||||
Specularity = true,
|
||||
@@ -200,6 +201,7 @@ namespace Barotrauma
|
||||
public float HUDScale;
|
||||
public float InventoryScale;
|
||||
public float LightMapScale;
|
||||
public int VisibleLightLimit;
|
||||
public float TextScale;
|
||||
public bool RadialDistortion;
|
||||
}
|
||||
|
||||
@@ -406,23 +406,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
public void Dispose()
|
||||
{
|
||||
if (!Disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
TargetComponents.Clear();
|
||||
}
|
||||
TargetComponents.Clear();
|
||||
}
|
||||
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,29 +396,20 @@ namespace Barotrauma
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
public override void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Prefabs.Remove(this);
|
||||
Prefabs.Remove(this);
|
||||
#if CLIENT
|
||||
Sprite?.Remove();
|
||||
Sprite = null;
|
||||
DecorativeSprites.ForEach(sprite => sprite.Remove());
|
||||
targetProperties.Clear();
|
||||
Sprite?.Remove();
|
||||
Sprite = null;
|
||||
DecorativeSprites.ForEach(sprite => sprite.Remove());
|
||||
targetProperties.Clear();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,6 @@ namespace Barotrauma
|
||||
{
|
||||
private readonly Dictionary<Identifier, Action<T>> events = new Dictionary<Identifier, Action<T>>();
|
||||
|
||||
~NamedEvent()
|
||||
{
|
||||
ReleaseUnmanagedResources();
|
||||
}
|
||||
|
||||
public void Register(Identifier identifier, Action<T> action)
|
||||
{
|
||||
if (HasEvent(identifier))
|
||||
@@ -53,15 +48,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources()
|
||||
public void Dispose()
|
||||
{
|
||||
events.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,8 +429,8 @@ namespace Barotrauma.IO
|
||||
|
||||
public class FileStream : System.IO.Stream
|
||||
{
|
||||
private System.IO.FileStream innerStream;
|
||||
private string fileName;
|
||||
private readonly System.IO.FileStream innerStream;
|
||||
private readonly string fileName;
|
||||
|
||||
public FileStream(string fn, System.IO.FileStream stream)
|
||||
{
|
||||
@@ -496,9 +496,9 @@ namespace Barotrauma.IO
|
||||
innerStream.Flush();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void Dispose(bool notCalledByFinalizer)
|
||||
{
|
||||
innerStream.Dispose();
|
||||
if (notCalledByFinalizer) { innerStream.Dispose(); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@ namespace Barotrauma
|
||||
{
|
||||
int read = 0;
|
||||
|
||||
// FIXME workaround for .NET6 causing save decompression to fail
|
||||
// BUG workaround for .NET6 causing save decompression to fail
|
||||
#if NET6_0
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,57 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.18.2.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Unstable only:
|
||||
- Fixed "submarine equality check failed" errors in non-campaign multiplayer game modes.
|
||||
- Fixed occasional crashes when exiting the sub editor.
|
||||
- Fixed diving suit lights being on when not worn.
|
||||
- Fixed research stations not working.
|
||||
- Adjusted the layout of server settings gameplay tab to prevent overlaps on small resolutions.
|
||||
- Fixed ignore orders not being loaded correctly in singleplayer.
|
||||
|
||||
Changes:
|
||||
- Lighting optimization: now some unimportant (dim and small) lights are hidden when there's lots of light sources visible on the screen at the same time. The maximum number of visible lights can be adjusted in the game settings.
|
||||
- Lighting optimization: the number of light recalculations per frame is limited, meaning that when there's lots of moving, shadow-casting lights visible, the game doesn't try to recalculate the shadows all at the same time.
|
||||
- Lighting optimization: simplify the light rendering when zoomed very far out (e.g. when looking through a periscope).
|
||||
- Optimized status effects that modify items' conditions every frame (for example, oxygen tank shelves that fill up oxygen tanks).
|
||||
- Hide AppData path from tooltips in the sub editor to prevent exposing the user's name.
|
||||
- Reduce nausea chance of energy drink to 25%.
|
||||
- Changes to the campaign progression in general.
|
||||
- Changes to the level generation parameters, especially in Cold Caverns and the Ridge.
|
||||
- Changes to the level resources distribution.
|
||||
- Changes to the event manager settings (that affect the monster spawns).
|
||||
- Adjusted and normalized the item loadouts for all the jobs.
|
||||
- Changes to the items that always spawn with the sub at the beginning of the game (start items).
|
||||
- Adjustments to the preferred containers (= where things are spawned and where they should be placed).
|
||||
- Changes to the existing missions and how they are distributed. Added new missions.
|
||||
- Reduced the costs for unlocking the biomes.
|
||||
- Minor adjustments to the monster spawns.
|
||||
- Changes to the item "gating". Some items don't appear early in the game anymore.
|
||||
- Adjustments to the mission specific variants of the monsters.
|
||||
- Added a large Crawler variant for some missions (removed the Swarmcrawler that was used for crawler missions).
|
||||
- Halved Mudraptors' priority for eating dead bodies.
|
||||
|
||||
Fixes:
|
||||
- Fixed abyss area being very small in the Aphotic Plateau, preventing the abyss monster from reaching you if you go deep enough.
|
||||
- Fixed status monitor displaying small amounts of water as 1% even though water detectors output 0%.
|
||||
- Fixed autopilot conflicting with VELOCITY_IN inputs (now signals override the autopilot for 1 second).
|
||||
- Fixed ConversationAction getting interrupted when opening an input-blocking menu in single player.
|
||||
- Fixed sprite bleed in chaingun ammunition boxes.
|
||||
- Fixed appearance of specific named NPCs being inconsistent (e.g. Captain Hognose sometimes being a woman or not having an eyepatch).
|
||||
- Fixed certain scripted events getting stuck if you switch characters in single player (e.g. the events that require you to interact with fliers on the wall).
|
||||
- Fixed crashing when the source of a rope is removed (e.g. when a latcher despawns while latched on to the sub).
|
||||
- Fixed votes always going through if no-one votes.
|
||||
- Fixed energy drink giving x10 more haste when used via the health interface.
|
||||
- Fixed the monster spawns for the new game plus not working (currently a placeholder set).
|
||||
- Fixed monsters spawning from missions not avoiding the engines.
|
||||
|
||||
Modding:
|
||||
- Level object, cave and mineral commonness can be defined based on the biome instead of the level generation parameters (= no need to define commonness for "coldcavernsbasic", "coldcavernsmaze" etc separately).
|
||||
- Option to define ConversationAction texts directly in the event xml (instead of having to always define them in a spearate text file).
|
||||
- Extended CustomInterface functionality with NumberInput elements that allow using float values ("numbertype") and defining the increment size ("step") the number of decimal places ("decimalplaces"). (Thanks, mLuby!)
|
||||
- Implemented <clear/> element for removing all the child elements of an element in a variant file.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.18.1.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user