Unstable v0.19.1.0

This commit is contained in:
Juan Pablo Arce
2022-08-19 13:59:08 -03:00
parent 6b55adcdd9
commit 1219615d64
192 changed files with 3875 additions and 2648 deletions
@@ -203,7 +203,7 @@ namespace Barotrauma.Items.Components
foreach ((Character character, Node node) in charactersInRange)
{
if (character == null || character.Removed) { continue; }
character.ApplyAttack(null, node.WorldPosition, attack, 1.0f);
character.ApplyAttack(null, node.WorldPosition, attack, MathHelper.Clamp(Voltage, 1.0f, MaxOverVoltageFactor));
}
}
DischargeProjSpecific();
@@ -295,12 +295,11 @@ namespace Barotrauma.Items.Components
if (attachable)
{
DeattachFromWall();
if (body != null)
{
item.body = body;
}
DeattachFromWall();
}
if (Pusher != null) { Pusher.Enabled = false; }
@@ -619,6 +618,10 @@ namespace Barotrauma.Items.Components
#if CLIENT
item.DrawDepthOffset = SpriteDepthWhenDropped - item.SpriteDepth;
#endif
foreach (LightComponent light in item.GetComponents<LightComponent>())
{
light.CheckIfNeedsUpdate();
}
}
public override void ParseMsg()
@@ -165,31 +165,32 @@ namespace Barotrauma.Items.Components
pickTimer = 0.0f;
while (pickTimer < requiredTime && Screen.Selected != GameMain.SubEditorScreen)
{
//cancel if the item is currently selected
//attempting to pick does not select the item, so if it is selected at this point, another ItemComponent
//must have been selected and we should not keep deattaching (happens when for example interacting with
//an electrical component while holding both a screwdriver and a wrench).
if (picker.IsAnySelectedItem(item)||
picker.IsKeyDown(InputType.Aim) ||
!picker.CanInteractWith(item) ||
item.Removed || item.ParentInventory != null)
if (!CoroutineManager.Paused)
{
StopPicking(picker);
yield return CoroutineStatus.Success;
}
//cancel if the item is currently selected
//attempting to pick does not select the item, so if it is selected at this point, another ItemComponent
//must have been selected and we should not keep deattaching (happens when for example interacting with
//an electrical component while holding both a screwdriver and a wrench).
if (picker.IsAnySelectedItem(item) ||
picker.IsKeyDown(InputType.Aim) ||
!picker.CanInteractWith(item) ||
item.Removed || item.ParentInventory != null)
{
StopPicking(picker);
yield return CoroutineStatus.Success;
}
#if CLIENT
Character.Controlled?.UpdateHUDProgressBar(
this,
item.WorldPosition,
pickTimer / requiredTime,
GUIStyle.Red, GUIStyle.Green,
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
Character.Controlled?.UpdateHUDProgressBar(
this,
item.WorldPosition,
pickTimer / requiredTime,
GUIStyle.Red, GUIStyle.Green,
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
#endif
picker.AnimController.UpdateUseItem(!picker.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
pickTimer += CoroutineManager.DeltaTime;
picker.AnimController.UpdateUseItem(!picker.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
pickTimer += CoroutineManager.DeltaTime;
}
yield return CoroutineStatus.Running;
}
@@ -96,7 +96,7 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
progressTimer += deltaTime * Math.Min(powerConsumption <= 0.0f ? 1 : Voltage, 1.0f);
progressTimer += deltaTime * Math.Min(powerConsumption <= 0.0f ? 1 : Voltage, MaxOverVoltageFactor);
float tinkeringStrength = 0f;
if (repairable.IsTinkering)
@@ -205,18 +205,18 @@ namespace Barotrauma.Items.Components
foreach (DeconstructItem deconstructProduct in products)
{
CreateDeconstructProduct(deconstructProduct, inputItems, amountMultiplier);
CreateDeconstructProduct(deconstructProduct, inputItems, (int)(amountMultiplier * deconstructProduct.Amount));
}
}
else
{
foreach (DeconstructItem deconstructProduct in validDeconstructItems)
{
CreateDeconstructProduct(deconstructProduct, inputItems, amountMultiplier);
CreateDeconstructProduct(deconstructProduct, inputItems, (int)(amountMultiplier * deconstructProduct.Amount));
}
}
void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems, float amountMultiplier)
void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems, int amount)
{
float percentageHealth = targetItem.Condition / targetItem.MaxCondition;
@@ -284,7 +284,6 @@ namespace Barotrauma.Items.Components
user.CheckTalents(AbilityEffectType.OnItemDeconstructedInventory, itemDeconstructedInventory);
}
int amount = (int)amountMultiplier;
for (int i = 0; i < amount; i++)
{
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
@@ -117,7 +117,7 @@ namespace Barotrauma.Items.Components
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
if (Math.Abs(Force) > 1.0f)
{
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, MaxOverVoltageFactor);
float currForce = force * voltageFactor;
float condition = item.Condition / item.MaxCondition;
// Broken engine makes more noise.
@@ -305,7 +305,7 @@ namespace Barotrauma.Items.Components
float fabricationSpeedIncrease = 1f + tinkeringStrength * TinkeringSpeedIncrease;
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(powerConsumption <= 0 ? 1 : Voltage, 1.0f);
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(powerConsumption <= 0 ? 1 : Voltage, MaxOverVoltageFactor);
UpdateRequiredTimeProjSpecific();
@@ -371,8 +371,7 @@ namespace Barotrauma.Items.Components
var availableItems = availableIngredients[requiredPrefab.Identifier];
var availableItem = availableItems.FirstOrDefault(potentialPrefab =>
{
return potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
return requiredItem.IsConditionSuitable(potentialPrefab.ConditionPercentage);
});
if (availableItem == null) { continue; }
@@ -616,8 +615,7 @@ namespace Barotrauma.Items.Components
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
foreach (Item availablePrefab in availablePrefabs)
{
if (availablePrefab.ConditionPercentage / 100.0f >= requiredItem.MinCondition &&
availablePrefab.ConditionPercentage / 100.0f <= requiredItem.MaxCondition)
if (requiredItem.IsConditionSuitable(availablePrefab.ConditionPercentage))
{
availablePrefabsAmount++;
}
@@ -732,9 +730,7 @@ namespace Barotrauma.Items.Components
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
var availablePrefab = availablePrefabs.FirstOrDefault(potentialPrefab =>
{
return !usedItems.Contains(potentialPrefab) &&
potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
return !usedItems.Contains(potentialPrefab) && requiredItem.IsConditionSuitable(potentialPrefab.ConditionPercentage);
});
if (availablePrefab == null) { continue; }
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -52,7 +51,7 @@ namespace Barotrauma.Items.Components
return;
}
CurrFlow = Math.Min(PowerConsumption > 0 ? Voltage : 1.0f, 1.0f) * generatedAmount * 100.0f;
CurrFlow = Math.Min(PowerConsumption > 0 ? Voltage : 1.0f, MaxOverVoltageFactor) * generatedAmount * 100.0f;
float conditionMult = item.Condition / item.MaxCondition;
//100% condition = 100% oxygen
//50% condition = 25% oxygen
@@ -130,7 +130,7 @@ namespace Barotrauma.Items.Components
if (item.CurrentHull == null) { return; }
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, 1.0f);
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, MaxOverVoltageFactor);
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
@@ -11,6 +11,8 @@ namespace Barotrauma.Items.Components
{
const float NetworkUpdateIntervalHigh = 0.5f;
const float TemperatureBoostAmount = 20;
//the rate at which the reactor is being run on (higher rate -> higher temperature)
private float fissionRate;
@@ -46,6 +48,8 @@ namespace Barotrauma.Items.Components
private Vector2 optimalFissionRate, allowedFissionRate;
private Vector2 optimalTurbineOutput, allowedTurbineOutput;
private float temperatureBoost;
private bool _powerOn;
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
@@ -241,6 +245,34 @@ namespace Barotrauma.Items.Components
}
#endif
if (signalControlledTargetFissionRate.HasValue && Math.Abs(signalControlledTargetFissionRate.Value - TargetFissionRate) > 1.0f)
{
TargetFissionRate = adjustValueWithoutOverShooting(TargetFissionRate, signalControlledTargetFissionRate.Value, deltaTime * 5.0f);
#if CLIENT
FissionRateScrollBar.BarScroll = TargetFissionRate / 100.0f;
#endif
}
else
{
signalControlledTargetFissionRate = null;
}
if (signalControlledTargetTurbineOutput.HasValue && Math.Abs(signalControlledTargetTurbineOutput.Value - TargetTurbineOutput) > 1.0f)
{
TargetTurbineOutput = adjustValueWithoutOverShooting(TargetTurbineOutput, signalControlledTargetTurbineOutput.Value, deltaTime * 5.0f);
#if CLIENT
TurbineOutputScrollBar.BarScroll = TargetTurbineOutput / 100.0f;
#endif
}
else
{
signalControlledTargetTurbineOutput = null;
}
static float adjustValueWithoutOverShooting(float current, float target, float speed)
{
return target < current ? Math.Max(target, current - speed) : Math.Min(target, current + speed);
}
prevAvailableFuel = AvailableFuel;
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
@@ -270,7 +302,10 @@ namespace Barotrauma.Items.Components
float temperatureDiff = (heatAmount - turbineOutput) - Temperature;
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
//if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;
temperatureBoost = adjustValueWithoutOverShooting(temperatureBoost, 0.0f, deltaTime);
#if CLIENT
temperatureBoostUpButton.Enabled = temperatureBoostDownButton.Enabled = Math.Abs(temperatureBoost) < TemperatureBoostAmount * 0.9f;
#endif
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(TargetFissionRate, AvailableFuel), deltaTime);
@@ -438,7 +473,7 @@ namespace Barotrauma.Items.Components
private float GetGeneratedHeat(float fissionRate)
{
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f;
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f + temperatureBoost;
}
/// <summary>
@@ -486,13 +521,15 @@ namespace Barotrauma.Items.Components
if (temperature > allowedTemperature.Y)
{
item.SendSignal("1", "meltdown_warning");
//faster meltdown if the item is in a bad condition
meltDownTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
if (meltDownTimer > MeltdownDelay)
if (!item.InvulnerableToDamage)
{
MeltDown();
return;
//faster meltdown if the item is in a bad condition
meltDownTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
if (meltDownTimer > MeltdownDelay)
{
MeltDown();
return;
}
}
}
else
@@ -797,30 +834,31 @@ namespace Barotrauma.Items.Components
AutoTemp = false;
TargetFissionRate = 0.0f;
TargetTurbineOutput = 0.0f;
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
registerUnsentChanges();
}
break;
case "set_fissionrate":
if (PowerOn && float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float newFissionRate))
{
TargetFissionRate = MathHelper.Clamp(newFissionRate, 0.0f, 100.0f);
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
#if CLIENT
FissionRateScrollBar.BarScroll = TargetFissionRate / 100.0f;
#endif
signalControlledTargetFissionRate = MathHelper.Clamp(newFissionRate, 0.0f, 100.0f);
registerUnsentChanges();
}
break;
case "set_turbineoutput":
if (PowerOn && float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float newTurbineOutput))
{
TargetTurbineOutput = MathHelper.Clamp(newTurbineOutput, 0.0f, 100.0f);
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
#if CLIENT
TurbineOutputScrollBar.BarScroll = TargetTurbineOutput / 100.0f;
#endif
signalControlledTargetTurbineOutput = MathHelper.Clamp(newTurbineOutput, 0.0f, 100.0f);
registerUnsentChanges();
}
break;
}
void registerUnsentChanges()
{
if (GameMain.NetworkMember is { IsServer: true }) { unsentChanges = true; }
}
}
private float? signalControlledTargetFissionRate, signalControlledTargetTurbineOutput;
}
}
@@ -98,7 +98,7 @@ namespace Barotrauma.Items.Components
set { maxRechargeSpeed = Math.Max(value, 1.0f); }
}
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "The current recharge speed of the device.")]
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The current recharge speed of the device.")]
public float RechargeSpeed
{
get { return rechargeSpeed; }
@@ -243,8 +243,13 @@ namespace Barotrauma.Items.Components
//damage the item if voltage is too high (except if running as a client)
float prevCondition = item.Condition;
item.Condition -= deltaTime * 10.0f;
//some randomness to prevent all junction boxes from breaking at the same time
if (Rand.Range(0.0f, 1.0f) < 0.01f)
{
//damaged boxes are more sensitive to overvoltage (also preventing all boxes from breaking at the same time)
float conditionFactor = MathHelper.Lerp(5.0f, 1.0f, item.Condition / item.MaxCondition);
item.Condition -= deltaTime * Rand.Range(10.0f, 500.0f) * conditionFactor;
}
if (item.Condition <= 0.0f && prevCondition > 0.0f)
{
overloadCooldownTimer = OverloadCooldown;
@@ -94,6 +94,11 @@ namespace Barotrauma.Items.Components
protected Connection powerIn, powerOut;
/// <summary>
/// Maximum voltage factor when the device is being overvolted. I.e. how many times more effectively the device can function when it's being overvolted
/// </summary>
protected const float MaxOverVoltageFactor = 2.0f;
protected virtual PowerPriority Priority { get { return PowerPriority.Default; } }
[Editable, Serialize(0.5f, IsPropertySaveable.Yes, description: "The minimum voltage required for the device to function. " +
@@ -244,6 +244,13 @@ namespace Barotrauma.Items.Components
private set;
}
[Serialize(false, IsPropertySaveable.No)]
public bool DamageDoors
{
get;
set;
}
public bool IsStuckToTarget => StickTarget != null;
private Category originalCollisionCategories;
@@ -288,7 +295,7 @@ namespace Barotrauma.Items.Components
}
}
private void Launch(Character user, Vector2 simPosition, float rotation, float damageMultiplier = 1f)
private void Launch(Character user, Vector2 simPosition, float rotation, float damageMultiplier = 1f, float launchImpulseModifier = 0f)
{
Item.body.ResetDynamics();
Item.SetTransform(simPosition, rotation);
@@ -299,7 +306,7 @@ namespace Barotrauma.Items.Components
// Set user for hitscan projectiles to work properly.
User = user;
// Need to set null for non-characterusable items.
Use(character: null);
Use(character: null, launchImpulseModifier);
// Set user for normal projectiles to work properly.
User = user;
if (Item.Removed) { return; }
@@ -312,7 +319,7 @@ namespace Barotrauma.Items.Components
}
}
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent, float damageMultiplier = 1f)
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent, float damageMultiplier = 1f, float launchImpulseModifier = 0f)
{
//add the limbs of the shooter to the list of bodies to be ignored
//so that the player can't shoot himself
@@ -320,7 +327,7 @@ namespace Barotrauma.Items.Components
Vector2 projectilePos = weaponPos;
//make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel
if (Submarine.PickBody(weaponPos, spawnPos, IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking,
customPredicate: (Fixture f) => { return !IgnoredBodies.Contains(f.Body); }) == null)
customPredicate: (Fixture f) => { return IgnoredBodies == null || !IgnoredBodies.Contains(f.Body); }) == null)
{
//no obstacles -> we can spawn the projectile at the barrel
projectilePos = spawnPos;
@@ -334,7 +341,7 @@ namespace Barotrauma.Items.Components
projectilePos = newPos;
}
}
Launch(user, projectilePos, rotation, damageMultiplier);
Launch(user, projectilePos, rotation, damageMultiplier, launchImpulseModifier);
if (createNetworkEvent && !Item.Removed && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
#if SERVER
@@ -344,7 +351,7 @@ namespace Barotrauma.Items.Components
}
}
public bool Use(Character character = null)
public bool Use(Character character = null, float launchImpulseModifier = 0f)
{
if (character != null && !characterUsable) { return false; }
@@ -379,7 +386,7 @@ namespace Barotrauma.Items.Components
else
{
item.body.SetTransform(item.body.SimPosition, launchAngle);
float modifiedLaunchImpulse = LaunchImpulse * (1 + Rand.Range(-ImpulseSpread, ImpulseSpread));
float modifiedLaunchImpulse = (LaunchImpulse + launchImpulseModifier) * (1 + Rand.Range(-ImpulseSpread, ImpulseSpread));
DoLaunch(launchDir * modifiedLaunchImpulse * item.body.Mass);
System.Diagnostics.Debug.WriteLine("launch: " + modifiedLaunchImpulse + " - " + item.body.LinearVelocity);
}
@@ -723,7 +730,7 @@ namespace Barotrauma.Items.Components
private bool OnProjectileCollision(Fixture f1, Fixture target, Contact contact)
{
if (User != null && User.Removed) { User = null; return false; }
if (IgnoredBodies.Contains(target.Body)) { return false; }
if (IgnoredBodies != null && IgnoredBodies.Contains(target.Body)) { return false; }
//ignore character colliders (the projectile only hits limbs)
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
{
@@ -828,7 +835,7 @@ namespace Barotrauma.Items.Components
private bool HandleProjectileCollision(Fixture target, Vector2 collisionNormal, Vector2 velocity)
{
if (User != null && User.Removed) { User = null; }
if (IgnoredBodies.Contains(target.Body)) { return false; }
if (IgnoredBodies != null && IgnoredBodies.Contains(target.Body)) { return false; }
//ignore character colliders (the projectile only hits limbs)
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
{
@@ -870,7 +877,7 @@ namespace Barotrauma.Items.Components
else if ((target.Body.UserData as Item ?? (target.Body.UserData as ItemComponent)?.Item) is Item targetItem)
{
if (targetItem.Removed) { return false; }
if (Attack != null && targetItem.Prefab.DamagedByProjectiles && targetItem.Condition > 0)
if (Attack != null && (targetItem.Prefab.DamagedByProjectiles || DamageDoors && targetItem.GetComponent<Door>() != null) && targetItem.Condition > 0)
{
attackResult = Attack.DoDamage(User ?? Attacker, targetItem, item.WorldPosition, 1.0f);
#if CLIENT
@@ -1067,7 +1074,7 @@ namespace Barotrauma.Items.Components
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
}
}
IgnoredBodies.Clear();
IgnoredBodies?.Clear();
}
private void StickToTarget(Body targetBody, Vector2 axis)
@@ -23,6 +23,9 @@ namespace Barotrauma.Items.Components
private readonly HashSet<Wire> wires;
public IReadOnlyCollection<Wire> Wires => wires;
private bool enumeratingWires;
private readonly HashSet<Wire> removedWires = new HashSet<Wire>();
private readonly Item item;
public readonly bool IsOutput;
@@ -239,7 +242,14 @@ namespace Barotrauma.Items.Components
}
prevOtherConnection.recipientsDirty = true;
}
wires.Remove(wire);
if (enumeratingWires)
{
removedWires.Add(wire);
}
else
{
wires.Remove(wire);
}
recipientsDirty = true;
}
@@ -278,6 +288,7 @@ namespace Barotrauma.Items.Components
public void SendSignal(Signal signal)
{
enumeratingWires = true;
foreach (var wire in wires)
{
Connection recipient = wire.OtherConnection(this);
@@ -301,6 +312,12 @@ namespace Barotrauma.Items.Components
}
}
}
enumeratingWires = false;
foreach (var removedWire in removedWires)
{
wires.Remove(removedWire);
}
removedWires.Clear();
}
public void ClearConnections()
@@ -313,13 +330,23 @@ namespace Barotrauma.Items.Components
Powered.ChangedConnections.Add(c);
}
}
foreach (var wire in wires)
{
wire.RemoveConnection(this);
recipientsDirty = true;
}
wires.Clear();
if (enumeratingWires)
{
foreach (var wire in wires)
{
removedWires.Add(wire);
}
}
else
{
wires.Clear();
}
}
public void InitializeFromLoaded()
@@ -247,9 +247,14 @@ namespace Barotrauma.Items.Components
}
public override void OnMapLoaded()
{
CheckIfNeedsUpdate();
}
public void CheckIfNeedsUpdate()
{
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null && IsOn &&
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
{
lightBrightness = 1.0f;
@@ -261,6 +266,10 @@ namespace Barotrauma.Items.Components
Light.ParentSub = item.Submarine;
#endif
}
else
{
IsActive = true;
}
}
public override void Update(float deltaTime, Camera cam)
@@ -21,12 +21,15 @@ namespace Barotrauma.Items.Components
private float rotation, targetRotation;
private float reload, reloadTime;
private float reload, reloadTime, delayBetweenBurst;
private int shotsPerBurst, shotCounter;
private float minRotation, maxRotation;
private float launchImpulse;
private float damageMultiplier;
private Camera cam;
private float angularVelocity;
@@ -94,6 +97,16 @@ namespace Barotrauma.Items.Components
get;
set;
}
public bool flipFiringOffset;
[Serialize(false, IsPropertySaveable.No, description: "If enabled, the firing offset will alternate from left to right (i.e. flipping the x-component of the offset each shot.)")]
public bool AlternatingFiringOffset
{
get;
set;
}
public Vector2 TransformedBarrelPos
{
get
@@ -116,6 +129,20 @@ namespace Barotrauma.Items.Components
set { reloadTime = value; }
}
[Editable(1, 100), Serialize(1, IsPropertySaveable.No, description: "How many projectiles needs to be shot before we add an extra break? Think of the double coilgun.")]
public int ShotsPerBurst
{
get { return shotsPerBurst; }
set { shotsPerBurst = value; }
}
[Editable(0.0f, 1000.0f, decimals: 3), Serialize(0.0f, IsPropertySaveable.No, description: "An extra delay between the bursts. Added to the reload.")]
public float DelayBetweenBursts
{
get { return delayBetweenBurst; }
set { delayBetweenBurst = value; }
}
[Editable(0.1f, 10f), Serialize(1.0f, IsPropertySaveable.No, description: "Modifies the duration of retraction of the barrell after recoil to get back to the original position after shooting. Reload time affects this too.")]
public float RetractionDurationMultiplier
{
@@ -137,6 +164,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(1.0f, IsPropertySaveable.No, description: "Multiplies the damage the turret deals by this amount.")]
public float DamageMultiplier
{
get { return damageMultiplier; }
set { damageMultiplier = value; }
}
[Serialize(1, IsPropertySaveable.No, description: "How many projectiles the weapon launches when fired once.")]
public int ProjectileCount
{
@@ -602,9 +636,9 @@ namespace Barotrauma.Items.Components
if (projectiles.Any())
{
ItemContainer projectileContainer = projectiles.First().Item.Container?.GetComponent<ItemContainer>();
if (projectileContainer != null && projectileContainer.Item != item)
{
projectileContainer?.Item.Use(deltaTime, null);
if (projectileContainer != null && projectileContainer.Item != item)
{
projectileContainer?.Item.Use(deltaTime, null);
}
}
else
@@ -764,6 +798,15 @@ namespace Barotrauma.Items.Components
private void Launch(Item projectile, Character user = null, float? launchRotation = null, float tinkeringStrength = 0f)
{
reload = reloadTime;
if (ShotsPerBurst > 1)
{
shotCounter++;
if (shotCounter >= ShotsPerBurst)
{
reload += DelayBetweenBursts;
shotCounter = 0;
}
}
reload /= 1f + (tinkeringStrength * TinkeringReloadDecrease);
if (user != null)
@@ -773,6 +816,10 @@ namespace Barotrauma.Items.Components
if (projectile != null)
{
if (AlternatingFiringOffset)
{
flipFiringOffset = !flipFiringOffset;
}
activeProjectiles.Add(projectile);
projectile.Drop(null, setTransform: false);
if (projectile.body != null)
@@ -796,9 +843,9 @@ namespace Barotrauma.Items.Components
projectileComponent.Attacker = projectileComponent.User = user;
if (projectileComponent.Attack != null)
{
projectileComponent.Attack.DamageMultiplier = 1f + (TinkeringDamageIncrease * tinkeringStrength);
projectileComponent.Attack.DamageMultiplier = (1f * DamageMultiplier) + (TinkeringDamageIncrease * tinkeringStrength);
}
projectileComponent.Use();
projectileComponent.Use(null, LaunchImpulse);
projectile.GetComponent<Rope>()?.Attach(item, projectile);
projectileComponent.User = user;
@@ -1452,7 +1499,9 @@ namespace Barotrauma.Items.Components
Vector2 transformedFiringOffset = Vector2.Zero;
if (useOffset)
{
transformedFiringOffset = MathUtils.RotatePoint(new Vector2(-FiringOffset.Y, -FiringOffset.X) * item.Scale, -rotation);
Vector2 currOffSet = FiringOffset;
if (flipFiringOffset) { currOffSet.X = -currOffSet.X; }
transformedFiringOffset = MathUtils.RotatePoint(new Vector2(-currOffSet.Y, -currOffSet.X) * item.Scale, -rotation);
}
return new Vector2(item.WorldRect.X + transformedBarrelPos.X + transformedFiringOffset.X, item.WorldRect.Y - transformedBarrelPos.Y + transformedFiringOffset.Y);
}
@@ -99,6 +99,7 @@ namespace Barotrauma
private bool hasComponentsToDraw;
public PhysicsBody body;
private float waterDragCoefficient;
public readonly XElement StaticBodyConfig;
@@ -860,12 +861,14 @@ namespace Barotrauma
SetActiveSprite();
ContentXElement bodyElement = null;
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "body":
float density = subElement.GetAttributeFloat("density", 10.0f);
bodyElement = subElement;
float density = subElement.GetAttributeFloat("density", Physics.NeutralDensity);
float minDensity = subElement.GetAttributeFloat("mindensity", density);
float maxDensity = subElement.GetAttributeFloat("maxdensity", density);
if (minDensity < maxDensity)
@@ -905,6 +908,7 @@ namespace Barotrauma
body = new PhysicsBody(subElement, ConvertUnits.ToSimUnits(Position), Scale, density, collisionCategory, collidesWith, findNewContacts: false);
body.FarseerBody.AngularDamping = subElement.GetAttributeFloat("angulardamping", 0.2f);
body.FarseerBody.LinearDamping = subElement.GetAttributeFloat("lineardamping", 0.1f);
body.FarseerBody.LinearDamping = subElement.GetAttributeFloat("lineardamping", 0.1f);
body.UserData = this;
break;
case "trigger":
@@ -994,6 +998,8 @@ namespace Barotrauma
if (body != null)
{
body.Submarine = submarine;
waterDragCoefficient = bodyElement.GetAttributeFloat("waterdragcoefficient",
GetComponent<Projectile>() != null || GetComponent<Throwable>() != null ? 0.1f : 1.0f);
}
//cache connections into a dictionary for faster lookups
@@ -1898,10 +1904,19 @@ namespace Barotrauma
if (needsWaterCheck)
{
bool wasInWater = inWater;
inWater = IsInWater();
bool waterProof = WaterProof;
if (inWater)
{
//the item has gone through the surface of the water
if (!wasInWater && CurrentHull != null && body != null && body.LinearVelocity.Y < -1.0f)
{
Splash();
//slow the item down (not physically accurate, but looks good enough)
body.LinearVelocity *= 0.2f;
}
Item container = this.Container;
while (!waterProof && container != null)
{
@@ -1929,7 +1944,8 @@ namespace Barotrauma
}
}
partial void Splash();
public void UpdateTransform()
{
if (body == null) { return; }
@@ -2017,23 +2033,47 @@ namespace Barotrauma
{
float floor = CurrentHull.Rect.Y - CurrentHull.Rect.Height;
float waterLevel = floor + CurrentHull.WaterVolume / CurrentHull.Rect.Width;
//forceFactor is 1.0f if the item is completely submerged,
//and goes to 0.0f as the item goes through the surface
forceFactor = Math.Min((waterLevel - Position.Y) / rect.Height, 1.0f);
if (forceFactor <= 0.0f) return;
if (forceFactor <= 0.0f) { return; }
}
bool moving = body.LinearVelocity.LengthSquared() > 0.001f;
float volume = body.Mass / body.Density;
if (moving)
{
//measure velocity from the velocity of the front of the item and apply the drag to the other end to get the drag to turn the item the "pointy end first"
var uplift = -GameMain.World.Gravity * forceFactor * volume;
//a more "proper" (but more expensive) way to do this would be to e.g. calculate the drag separately for each edge of the fixture
//but since we define the "front" as the "pointy end", we can cheat a bit by using that, and actually even make the drag appear more realistic in some cases
//(e.g. a bullet with a rectangular fixture would be just as "aerodynamic" travelling backwards, but with this method we get it to turn the correct way)
Vector2 localFront = body.GetLocalFront();
Vector2 frontVel = body.FarseerBody.GetLinearVelocityFromLocalPoint(localFront);
Vector2 drag = body.LinearVelocity * volume;
float speed = frontVel.Length();
float drag = speed * speed * waterDragCoefficient * volume * Physics.NeutralDensity;
//very small drag on active projectiles to prevent affecting their trajectories much
if (body.FarseerBody.IsBullet) { drag *= 0.1f; }
Vector2 dragVec = -frontVel / speed * drag;
body.ApplyForce((uplift - drag) * 10.0f);
//apply the force slightly towards the back of the item to make it turn the front first
Vector2 back = body.FarseerBody.GetWorldPoint(-localFront * 0.01f);
body.ApplyForce(dragVec, back);
}
//no need to apply buoyancy if the item is still and not light enough to float
if (moving || body.Density < 10.0f)
{
Vector2 buoyancy = -GameMain.World.Gravity * forceFactor * volume * Physics.NeutralDensity;
body.ApplyForce(buoyancy);
}
//apply simple angular drag
body.ApplyTorque(body.AngularVelocity * volume * -0.05f);
if (Math.Abs(body.AngularVelocity) > 0.0001f)
{
body.ApplyTorque(body.AngularVelocity * volume * -0.1f);
}
}
@@ -3270,7 +3310,7 @@ namespace Barotrauma
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) * scaleRelativeToPrefab).ToPoint();
item.rect.Location -= (origin - oldOrigin).ToPoint();
}
if (item.PurchasedNewSwap && !string.IsNullOrEmpty(appliedSwap.SwappableItem?.SpawnWithId))
@@ -14,6 +14,8 @@ namespace Barotrauma
readonly struct DeconstructItem
{
public readonly Identifier ItemIdentifier;
//number of items to output
public readonly int Amount;
//minCondition does <= check, meaning that below or equal to min condition will be skipped.
public readonly float MinCondition;
//maxCondition does > check, meaning that above this max the deconstruct item will be skipped.
@@ -37,6 +39,7 @@ namespace Barotrauma
public DeconstructItem(XElement element, Identifier parentDebugName)
{
ItemIdentifier = element.GetAttributeIdentifier("identifier", "");
Amount = element.GetAttributeInt("amount", 1);
MinCondition = element.GetAttributeFloat("mincondition", -0.1f);
MaxCondition = element.GetAttributeFloat("maxcondition", 1.0f);
OutConditionMin = element.GetAttributeFloat("outconditionmin", element.GetAttributeFloat("outcondition", 1.0f));
@@ -70,6 +73,20 @@ namespace Barotrauma
public readonly float MinCondition;
public readonly float MaxCondition;
public readonly bool UseCondition;
public bool IsConditionSuitable(float conditionPercentage)
{
float normalizedCondition = conditionPercentage / 100.0f;
if (MathUtils.NearlyEqual(normalizedCondition, MinCondition) || MathUtils.NearlyEqual(normalizedCondition, MaxCondition))
{
return true;
}
else if (normalizedCondition >= MinCondition && normalizedCondition <= MaxCondition)
{
return true;
}
return false;
}
}
public class RequiredItemByIdentifier : RequiredItem
@@ -393,12 +410,106 @@ namespace Barotrauma
private set;
}
public readonly struct CommonnessInfo
{
public float Commonness
{
get
{
return commonness;
}
}
public float AbyssCommonness
{
get
{
return abyssCommonness ?? 0.0f;
}
}
public float CaveCommonness
{
get
{
return caveCommonness ?? Commonness;
}
}
public bool CanAppear
{
get
{
if (Commonness > 0.0f) { return true; }
if (AbyssCommonness > 0.0f) { return true; }
if (CaveCommonness > 0.0f) { return true; }
return false;
}
}
public readonly float commonness;
public readonly float? abyssCommonness;
public readonly float? caveCommonness;
public CommonnessInfo(XElement element)
{
this.commonness = Math.Max(element?.GetAttributeFloat("commonness", 0.0f) ?? 0.0f, 0.0f);
float? abyssCommonness = null;
XAttribute abyssCommonnessAttribute = element?.GetAttribute("abysscommonness") ?? element?.GetAttribute("abyss");
if (abyssCommonnessAttribute != null)
{
abyssCommonness = Math.Max(abyssCommonnessAttribute.GetAttributeFloat(0.0f), 0.0f);
}
this.abyssCommonness = abyssCommonness;
float? caveCommonness = null;
XAttribute caveCommonnessAttribute = element?.GetAttribute("cavecommonness") ?? element?.GetAttribute("cave");
if (caveCommonnessAttribute != null)
{
caveCommonness = Math.Max(caveCommonnessAttribute.GetAttributeFloat(0.0f), 0.0f);
}
this.caveCommonness = caveCommonness;
}
public CommonnessInfo(float commonness, float? abyssCommonness, float? caveCommonness)
{
this.commonness = commonness;
this.abyssCommonness = abyssCommonness != null ? (float?)Math.Max(abyssCommonness.Value, 0.0f) : null;
this.caveCommonness = caveCommonness != null ? (float?)Math.Max(caveCommonness.Value, 0.0f) : null;
}
public CommonnessInfo WithInheritedCommonness(CommonnessInfo? parentInfo)
{
return new CommonnessInfo(commonness,
abyssCommonness ?? parentInfo?.abyssCommonness,
caveCommonness ?? parentInfo?.caveCommonness);
}
public CommonnessInfo WithInheritedCommonness(params CommonnessInfo?[] parentInfos)
{
CommonnessInfo info = this;
foreach (var parentInfo in parentInfos)
{
info = info.WithInheritedCommonness(parentInfo);
}
return info;
}
public float GetCommonness(Level.TunnelType tunnelType)
{
if (tunnelType == Level.TunnelType.Cave)
{
return CaveCommonness;
}
else
{
return Commonness;
}
}
}
/// <summary>
/// How likely it is for the item to spawn in a level of a given type.
/// Key = name of the LevelGenerationParameters (empty string = default value) /* TODO: empty string = default value???? */
/// Value = commonness
/// </summary>
public ImmutableDictionary<Identifier, float> LevelCommonness { get; private set; }
private ImmutableDictionary<Identifier, CommonnessInfo> LevelCommonness { get; set; }
public readonly struct FixedQuantityResourceInfo
{
@@ -747,7 +858,7 @@ namespace Barotrauma
AllowDroppingOnSwapWith = allowDroppingOnSwapWith.ToImmutableHashSet();
AllowDroppingOnSwap = allowDroppingOnSwapWith.Any();
var levelCommonness = new Dictionary<Identifier, float>();
var levelCommonness = new Dictionary<Identifier, CommonnessInfo>();
var levelQuantity = new Dictionary<Identifier, FixedQuantityResourceInfo>();
foreach (ContentXElement subElement in ConfigElement.Elements())
@@ -871,7 +982,7 @@ namespace Barotrauma
{
if (!levelCommonness.ContainsKey(levelName))
{
levelCommonness.Add(levelName, levelCommonnessElement.GetAttributeFloat("commonness", 0.0f));
levelCommonness.Add(levelName, new CommonnessInfo(levelCommonnessElement));
}
}
else
@@ -962,6 +1073,40 @@ namespace Barotrauma
this.allowedLinks = ConfigElement.GetAttributeIdentifierArray("allowedlinks", Array.Empty<Identifier>()).ToImmutableHashSet();
}
public CommonnessInfo? GetCommonnessInfo(Level level)
{
CommonnessInfo? levelCommonnessInfo = GetValueOrNull(level.GenerationParams.Identifier);
CommonnessInfo? biomeCommonnessInfo = GetValueOrNull(level.LevelData.Biome.Identifier);
CommonnessInfo? defaultCommonnessInfo = GetValueOrNull(Identifier.Empty);
if (levelCommonnessInfo.HasValue)
{
return levelCommonnessInfo?.WithInheritedCommonness(biomeCommonnessInfo, defaultCommonnessInfo);
}
else if (biomeCommonnessInfo.HasValue)
{
return biomeCommonnessInfo?.WithInheritedCommonness(defaultCommonnessInfo);
}
else if (defaultCommonnessInfo.HasValue)
{
return defaultCommonnessInfo;
}
return null;
CommonnessInfo? GetValueOrNull(Identifier identifier)
{
if (LevelCommonness.TryGetValue(identifier, out CommonnessInfo info))
{
return info;
}
else
{
return null;
}
}
}
public float GetTreatmentSuitability(Identifier treatmentIdentifier)
{
return treatmentSuitability.TryGetValue(treatmentIdentifier, out float suitability) ? suitability : 0.0f;