Unstable 0.1500.6.0 (1984 edition)

This commit is contained in:
Markus Isberg
2021-10-09 00:22:02 +09:00
parent 08bdfc6cea
commit c8943ef9c4
96 changed files with 1817 additions and 559 deletions
@@ -0,0 +1,280 @@
#nullable enable
using System;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
namespace Barotrauma.Items.Components
{
internal partial class EntitySpawnerComponent : ItemComponent, IDrawableComponent
{
public enum AreaShape
{
Rectangle,
Circle
}
[Editable, Serialize("", true, "Identifier of the item to spawn, does nothing if SpeciesName is set. Separate by comma to have multiple items spawn at random.")]
public string? ItemIdentifier { get; set; }
[Editable, Serialize("", true, "Species name of the creature to spawn, takes priority if ItemIdentifier is set. Separate by comma to have multiple creatures spawn at random.")]
public string? SpeciesName { get; set; }
[Editable, Serialize(true, true, "Only spawn if crew members are within certain area")]
public bool OnlySpawnWhenCrewInRange { get; set; }
[Editable, Serialize(AreaShape.Rectangle, true, "Shape of the area where crew members need to stay")]
public AreaShape CrewAreaShape { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", true, "Size of the rectangle where crew members need to stay. Does nothing if CrewAreaShape is set to Circle")]
public Vector2 CrewAreaBounds { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Radius of the circle to spawn stuff in. Does nothing if CrewAreaShape is set to Rectangle")]
public float CrewAreaRadius { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the crew area from the center of the item")]
public Vector2 CrewAreaOffset { get; set; }
[Editable, Serialize(AreaShape.Rectangle, true, "Shape of the area where enemies or items are spawned")]
public AreaShape SpawnAreaShape { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", true, "Size of the rectangle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Circle")]
public Vector2 SpawnAreaBounds { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Radius of the circle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Rectangle")]
public float SpawnAreaRadius { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the spawn area from the center of the item")]
public Vector2 SpawnAreaOffset { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", true, "Time range between spawn attempts in seconds")]
public Vector2 SpawnTimerRange { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", true, "Minumum and maximum amount of items or creatures to spawn in one attempt")]
public Vector2 SpawnAmountRange { get; set; }
[Editable(MinValueInt = 0), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned")]
public int MaximumAmount { get; set; }
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
public float MaximumAmountRangePadding { get; set; }
[Serialize(true, true, "")]
public bool CanSpawn { get; set; } = true;
private float SpawnTimer;
private float? SpawnTimerGoal;
public EntitySpawnerComponent(Item item, XElement element) : base(item, element)
{
IsActive = true;
}
public override void OnItemLoaded()
{
if (!string.IsNullOrWhiteSpace(ItemIdentifier))
{
string[] allItems = ItemIdentifier.Split(',');
foreach (string itemIdentifier in allItems)
{
string trimmedString = itemIdentifier.Trim();
bool found = false;
foreach (ItemPrefab prefab in ItemPrefab.Prefabs)
{
if (string.Equals(trimmedString, prefab.Identifier, StringComparison.OrdinalIgnoreCase))
{
found = true;
break;
}
}
if (!found)
{
DebugConsole.ThrowError($"Error loading {nameof(EntitySpawnerComponent)} - item prefab \"" + name + "\" (identifier \"" + trimmedString + "\") not found.");
}
}
}
base.OnItemLoaded();
}
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
item.SendSignal(CanSpawn ? "1" : "0", "state_out");
if (GameMain.NetworkMember is { IsClient: true }) { return; }
SpawnTimerGoal ??= Rand.Range(Math.Min(SpawnTimerRange.X, SpawnTimerRange.Y), Math.Max(SpawnTimerRange.X, SpawnTimerRange.Y), Rand.RandSync.Unsynced);
SpawnTimer += deltaTime;
if (SpawnTimer > SpawnTimerGoal)
{
Spawn();
SpawnTimerGoal = null;
SpawnTimer = 0;
}
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
bool isNonZero = signal.value != "0";
switch (connection.Name)
{
case "set_state":
CanSpawn = isNonZero;
break;
case "toggle" when isNonZero:
CanSpawn = !CanSpawn;
break;
}
}
private RectangleF GetAreaRectangle(Vector2 size, Vector2 offset, bool draw)
{
Vector2 pos = item.WorldPosition;
if (draw)
{
pos.Y = -pos.Y;
}
pos += offset;
RectangleF rect = new RectangleF(pos.X - size.X / 2f, pos.Y - size.Y / 2f, size.X, size.Y);
return rect;
}
private bool CanSpawnMore()
{
if (!CanSpawn) { return false; }
if (OnlySpawnWhenCrewInRange)
{
if (!Character.CharacterList.Any(c => !c.IsDead && c.IsOnPlayerTeam && IsInRange(c.WorldPosition, crewArea: true, rangePad: false)))
{
return false;
}
}
int amount;
if (!string.IsNullOrWhiteSpace(SpeciesName))
{
amount = Character.CharacterList.Count(c => !c.IsDead && c.SpeciesName.Equals(SpeciesName, StringComparison.OrdinalIgnoreCase) && IsInRange(c.WorldPosition, crewArea: false, rangePad: true));
}
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
{
amount = Item.ItemList.Count(it => it.Submarine == item.Submarine && it.Prefab.Identifier.Equals(ItemIdentifier, StringComparison.OrdinalIgnoreCase) && IsInRange(it.WorldPosition, crewArea: false, rangePad: true));
}
else
{
return false;
}
return amount < MaximumAmount;
}
private bool IsInRange(Vector2 worldPos, bool crewArea = false, bool rangePad = false)
{
Vector2 offset = crewArea ? CrewAreaOffset : SpawnAreaOffset;
offset.Y = -offset.Y;
switch (crewArea ? CrewAreaShape : SpawnAreaShape)
{
case AreaShape.Circle:
Vector2 center = item.WorldPosition + offset;
float distance = (crewArea ? CrewAreaRadius : SpawnAreaRadius) + (rangePad ? MaximumAmountRangePadding : 0);
return Vector2.DistanceSquared(worldPos, center) < distance * distance;
case AreaShape.Rectangle:
RectangleF rect = GetAreaRectangle(crewArea ? CrewAreaBounds : SpawnAreaBounds, offset, draw: false);
if (rangePad)
{
rect.Inflate(MaximumAmountRangePadding, MaximumAmountRangePadding);
}
return rect.Contains(worldPos);
}
return false;
}
public void Spawn()
{
if (!CanSpawnMore()) { return; }
int minAmount = Math.Min((int)SpawnAmountRange.X, (int)SpawnAmountRange.Y),
maxAmount = Math.Max((int)SpawnAmountRange.X, (int)SpawnAmountRange.Y);
int amount = Rand.Range(minAmount, maxAmount, Rand.RandSync.Unsynced);
Vector2 offset = SpawnAreaOffset;
offset.Y = -offset.Y;
switch (SpawnAreaShape)
{
case AreaShape.Circle:
{
var (x, y) = item.WorldPosition + offset;
for (int i = 0; i < Math.Max(1, amount); i++)
{
float angle = Rand.Range(-MathHelper.TwoPi, MathHelper.TwoPi);
float distance = Rand.Range(0, SpawnAreaRadius, Rand.RandSync.Unsynced);
Vector2 spawnPos = new Vector2(x + distance * (float)Math.Cos(angle), y + distance * (float)Math.Sin(angle));
SpawnEntity(spawnPos);
}
break;
}
case AreaShape.Rectangle:
{
RectangleF rect = GetAreaRectangle(SpawnAreaBounds, offset, draw: false);
for (int i = 0; i < Math.Max(1, amount); i++)
{
float minX = Math.Min(rect.Left, rect.Right),
maxX = Math.Max(rect.Left, rect.Right),
minY = Math.Min(rect.Top, rect.Bottom),
maxY = Math.Max(rect.Top, rect.Bottom);
Vector2 spawnPos = new Vector2(Rand.Range(minX, maxX, Rand.RandSync.Unsynced), Rand.Range(minY, maxY, Rand.RandSync.Unsynced));
SpawnEntity(spawnPos);
}
break;
}
}
void SpawnEntity(Vector2 pos)
{
if (!string.IsNullOrWhiteSpace(SpeciesName))
{
string[] allSpecies = SpeciesName.Split(',');
string species = allSpecies.GetRandom().Trim();
Entity.Spawner.AddToSpawnQueue(species, pos);
}
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
{
string[] allItems = ItemIdentifier.Split(',');
string itemIdentifier = allItems.GetRandom().Trim();
ItemPrefab? prefab = ItemPrefab.Find(null, itemIdentifier);
if (prefab is null) { return; }
if (item.Submarine is { } sub)
{
pos -= sub.Position;
}
Entity.Spawner.AddToSpawnQueue(prefab, pos, item.Submarine);
}
}
}
}
}
@@ -598,9 +598,12 @@ namespace Barotrauma.Items.Components
DebugConsole.AddWarning("Character without CharacterInfo attempting to attach a limited attachable item!");
return false;
}
Vector2 attachPos = GetAttachPosition(character, useWorldCoordinates: true);
Structure attachTarget = Structure.GetAttachTarget(attachPos);
int maxAttachableCount = (int)character.Info.GetSavedStatValue(StatTypes.MaxAttachableCount, item.Prefab.Identifier);
int currentlyAttachedCount = Item.ItemList.Count(
i => i.Submarine == item.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.prefab.Identifier);
i => i.Submarine == attachTarget?.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.prefab.Identifier);
if (currentlyAttachedCount >= maxAttachableCount)
{
#if CLIENT
@@ -77,6 +77,7 @@ namespace Barotrauma.Items.Components
};
}
item.IsShootable = true;
item.RequireAimToUse = element.Parent.GetAttributeBool("requireaimtouse", true);
PreferredContainedItems = element.GetAttributeStringArray("preferredcontaineditems", new string[0], convertToLowerInvariant: true);
}
@@ -210,7 +211,7 @@ namespace Barotrauma.Items.Components
bool aim = item.RequireAimToUse && picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && picker.CanAim;
if (aim)
{
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 5f, MathHelper.PiOver4));
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 3f, MathHelper.PiOver4));
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos, aimMelee: true);
}
else
@@ -222,16 +223,16 @@ namespace Barotrauma.Items.Components
else
{
// TODO: We might want to make this configurable
hitPos = MathUtils.WrapAnglePi(hitPos - deltaTime * 15f);
hitPos -= deltaTime * 15f;
if (Swing)
{
ac.HoldItem(deltaTime, item, handlePos, SwingPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos);
ac.HoldItem(deltaTime, item, handlePos, SwingPos, Vector2.Zero, aim: false, hitPos, holdAngle);
}
else
{
ac.HoldItem(deltaTime, item, handlePos, holdPos, Vector2.Zero, aim: false, holdAngle);
}
if (hitPos < -MathHelper.PiOver2)
if (hitPos < -MathHelper.Pi)
{
RestoreCollision();
hitting = false;
@@ -74,8 +74,8 @@ namespace Barotrauma.Items.Components
{
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation);
Vector2 flippedPos = barrelPos;
if (item.body.Dir < 0.0f) flippedPos.X = -flippedPos.X;
return Vector2.Transform(flippedPos, bodyTransform);
if (item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
return Vector2.Transform(flippedPos, bodyTransform) * item.Scale;
}
}
@@ -712,7 +712,7 @@ namespace Barotrauma.Items.Components
humanAnim.Crouching = true;
}
}
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.inWater))
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.InWater))
{
// Steer closer
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
@@ -522,7 +522,7 @@ namespace Barotrauma.Items.Components
if (Math.Abs(item.Rotation) > 0.01f)
{
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
transformedItemPos = Vector2.Transform(transformedItemPos - item.Position, transform) + item.Position;
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
transformedItemIntervalVertical = Vector2.Transform(transformedItemIntervalVertical, transform);
@@ -550,6 +550,10 @@ namespace Barotrauma.Items.Components
currentRotation *= item.body.Dir;
currentRotation += item.body.Rotation;
}
else
{
currentRotation += MathHelper.ToRadians(-item.Rotation);
}
int i = 0;
Vector2 currentItemPos = transformedItemPos;
@@ -64,13 +64,6 @@ namespace Barotrauma.Items.Components
set;
}
[Editable, Serialize(true, true, description: "Enable hull condition mode.")]
public bool EnableHullCondition
{
get;
set;
}
[Editable, Serialize(true, true, description: "Enable item finder mode.")]
public bool EnableItemFinder
{
@@ -148,30 +141,49 @@ namespace Barotrauma.Items.Components
hullDatas.Add(sourceHull, hullData);
}
if (hullData.Distort) return;
if (hullData.Distort) { return; }
switch (connection.Name)
{
case "water_data_in":
//cheating a bit because water detectors don't actually send the water level
float waterAmount;
if (source.GetComponent<WaterDetector>() == null)
{
hullData.ReceivedWaterAmount = Rand.Range(0.0f, 1.0f);
waterAmount = Rand.Range(0.0f, 1.0f);
}
else
{
hullData.ReceivedWaterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
waterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
}
hullData.ReceivedWaterAmount = waterAmount;
foreach (var linked in sourceHull.linkedTo)
{
if (!(linked is Hull linkedHull)) { continue; }
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
{
linkedHullData = new HullData();
hullDatas.Add(linkedHull, linkedHullData);
}
linkedHullData.ReceivedWaterAmount = waterAmount;
}
break;
case "oxygen_data_in":
float oxy;
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out oxy))
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float oxy))
{
oxy = Rand.Range(0.0f, 100.0f);
}
hullData.ReceivedOxygenAmount = oxy;
foreach (var linked in sourceHull.linkedTo)
{
if (!(linked is Hull linkedHull)) { continue; }
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
{
linkedHullData = new HullData();
hullDatas.Add(linkedHull, linkedHullData);
}
linkedHullData.ReceivedOxygenAmount = oxy;
}
break;
}
}
@@ -9,6 +9,14 @@ namespace Barotrauma.Items.Components
{
public const int MaxQuality = 3;
public static readonly float[] QualityCommonnesses = new float[]
{
0.8f,
0.15f,
0.045f,
0.005f,
};
public enum StatType
{
Condition,
@@ -39,7 +47,18 @@ namespace Barotrauma.Items.Components
public int QualityLevel
{
get { return qualityLevel; }
set { qualityLevel = MathHelper.Clamp(value, 0, MaxQuality); }
set
{
if (value == qualityLevel) { return; }
bool wasInFullCondition = item.IsFullCondition;
qualityLevel = MathHelper.Clamp(value, 0, MaxQuality);
//set the condition to the new max condition
if (wasInFullCondition && statValues.ContainsKey(StatType.Condition))
{
item.Condition = item.MaxCondition;
}
}
}
public Quality(Item item, XElement element) : base(item, element)
@@ -376,7 +376,7 @@ namespace Barotrauma.Items.Components
tinkeringDuration -= deltaTime;
// not great to interject it here, should be less reliant on returning
float conditionDecrease = deltaTime * (CurrentFixer.GetStatValue(StatTypes.TinkeringDamage) / item.MaxCondition) * 100f;
float conditionDecrease = deltaTime * (CurrentFixer.GetStatValue(StatTypes.TinkeringDamage) / item.Prefab.Health) * 100f;
item.Condition -= conditionDecrease;
if (!CanTinker(CurrentFixer) || tinkeringDuration <= 0f)
@@ -424,7 +424,8 @@ namespace Barotrauma.Items.Components
}
else
{
float conditionIncrease = deltaTime / (fixDuration / item.MaxCondition);
// scale with prefab's health instead of real health to ensure repair speed remains static with upgrades
float conditionIncrease = deltaTime / (fixDuration / item.Prefab.Health);
item.Condition += conditionIncrease;
#if SERVER
GameMain.Server.KarmaManager.OnItemRepaired(CurrentFixer, this, conditionIncrease);
@@ -458,7 +459,8 @@ namespace Barotrauma.Items.Components
}
else
{
float conditionDecrease = deltaTime / (fixDuration / item.MaxCondition);
// scale with prefab's health instead of real health to ensure sabotage speed remains static with (any) upgrades
float conditionDecrease = deltaTime / (fixDuration / item.Prefab.Health);
item.Condition -= conditionDecrease;
}
@@ -159,7 +159,10 @@ namespace Barotrauma.Items.Components
{
lightColor = value;
#if CLIENT
if (Light != null) Light.Color = IsActive ? lightColor : Color.Transparent;
if (Light != null)
{
Light.Color = IsActive ? lightColor : Color.Transparent;
}
#endif
}
}
@@ -178,7 +178,7 @@ namespace Barotrauma.Items.Components
//signal strength diminishes by distance
float sentSignalStrength = signal.strength *
MathHelper.Clamp(1.0f - (Vector2.Distance(item.WorldPosition, wifiComp.item.WorldPosition) / wifiComp.range), 0.0f, 1.0f);
Signal s = new Signal(signal.value, ++signal.stepsTaken, sender: signal.sender, source: signal.source,
Signal s = new Signal(signal.value, signal.stepsTaken + 1, sender: signal.sender, source: signal.source,
power: 0.0f, strength: sentSignalStrength);
if (wifiComp.signalOutConnection != null)