(bf212a41f) v0.9.2.0 pre-release test version
This commit is contained in:
@@ -64,6 +64,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float SonarDisruption
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string SonarLabel;
|
||||
|
||||
public bool Enabled = true;
|
||||
@@ -127,6 +133,7 @@ namespace Barotrauma
|
||||
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
|
||||
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
|
||||
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
|
||||
SonarDisruption = element.GetAttributeFloat("sonardisruption", 0.0f);
|
||||
SonarLabel = element.GetAttributeString("sonarlabel", "");
|
||||
string typeString = element.GetAttributeString("type", "Any");
|
||||
if (Enum.TryParse(typeString, out TargetType t))
|
||||
|
||||
@@ -841,7 +841,7 @@ namespace Barotrauma
|
||||
private Limb GetAttackLimb(Vector2 attackWorldPos, Limb ignoredLimb = null)
|
||||
{
|
||||
AttackContext currentContext = Character.GetAttackContext();
|
||||
var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget.Entity;
|
||||
var target = wallTarget != null ? wallTarget.Structure : SelectedAiTarget?.Entity;
|
||||
Limb selectedLimb = null;
|
||||
float currentPriority = 0;
|
||||
foreach (Limb limb in Character.AnimController.Limbs)
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace Barotrauma
|
||||
{
|
||||
Weapon = null;
|
||||
}
|
||||
else if (!WeaponComponent.HasRequiredContainedItems(false))
|
||||
else if (!WeaponComponent.HasRequiredContainedItems(character, addMessage: false))
|
||||
{
|
||||
// Seek ammunition only if cannot find a new weapon
|
||||
if (!Reload(!HoldPosition, () => GetWeapon(out _) == null))
|
||||
@@ -234,14 +234,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (component is RangedWeapon rw)
|
||||
{
|
||||
if (ignoreRequiredItems || rw.HasRequiredContainedItems(false))
|
||||
if (ignoreRequiredItems || rw.HasRequiredContainedItems(character, addMessage: false))
|
||||
{
|
||||
weapons.Add(rw);
|
||||
}
|
||||
}
|
||||
else if (component is MeleeWeapon mw)
|
||||
{
|
||||
if (ignoreRequiredItems || mw.HasRequiredContainedItems(false))
|
||||
if (ignoreRequiredItems || mw.HasRequiredContainedItems(character, addMessage: false))
|
||||
{
|
||||
weapons.Add(mw);
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (statusEffect.Afflictions.Any())
|
||||
{
|
||||
if (ignoreRequiredItems || component.HasRequiredContainedItems(false))
|
||||
if (ignoreRequiredItems || component.HasRequiredContainedItems(character, addMessage: false))
|
||||
{
|
||||
weapons.Add(component);
|
||||
}
|
||||
@@ -284,7 +284,7 @@ namespace Barotrauma
|
||||
private bool Equip()
|
||||
{
|
||||
if (character.LockHands) { return false; }
|
||||
if (!WeaponComponent.HasRequiredContainedItems(false))
|
||||
if (!WeaponComponent.HasRequiredContainedItems(character, addMessage: false))
|
||||
{
|
||||
Mode = CombatMode.Retreat;
|
||||
return false;
|
||||
@@ -428,7 +428,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (WeaponComponent.HasRequiredContainedItems(false))
|
||||
if (WeaponComponent.HasRequiredContainedItems(character, addMessage: false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
@@ -108,6 +108,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
move = false;
|
||||
character.SetInput(extinguisher.Item.IsShootable ? InputType.Shoot : InputType.Use, false, true);
|
||||
extinguisher.Use(deltaTime, character);
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Barotrauma
|
||||
bool remove = false;
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (!ic.HasRequiredContainedItems(addMessage: false)) { continue; }
|
||||
if (!ic.HasRequiredContainedItems(user: character, addMessage: false)) { continue; }
|
||||
#if CLIENT
|
||||
ic.PlaySound(ActionType.OnUse, character.WorldPosition, character);
|
||||
#endif
|
||||
|
||||
@@ -50,23 +50,13 @@ namespace Barotrauma
|
||||
|
||||
if (!IsRemotePlayer)
|
||||
{
|
||||
float characterDist = Vector2.DistanceSquared(cam.WorldViewCenter, WorldPosition);
|
||||
#if SERVER
|
||||
float characterDist = float.MaxValue;
|
||||
#if CLIENT
|
||||
characterDist = Vector2.DistanceSquared(cam.GetPosition(), WorldPosition);
|
||||
#elif SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
//get the distance from the closest player to this character
|
||||
foreach (Character c in CharacterList)
|
||||
{
|
||||
if (c != this && c.IsRemotePlayer)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(c.WorldPosition, WorldPosition);
|
||||
if (dist < characterDist)
|
||||
{
|
||||
characterDist = dist;
|
||||
if (characterDist < DisableSimplePhysicsDistSqr) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
characterDist = GetClosestDistance();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -90,5 +80,50 @@ namespace Barotrauma
|
||||
aiController.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
// Gets the closest distance, either an active player character or spectator
|
||||
private float GetClosestDistance()
|
||||
{
|
||||
float minDist = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < GameMain.Server.ConnectedClients.Count; i++)
|
||||
{
|
||||
var spectatePos = GameMain.Server.ConnectedClients[i].SpectatePos;
|
||||
if (spectatePos != null)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(spectatePos.Value, WorldPosition);
|
||||
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
}
|
||||
if (dist < DisableSimplePhysicsDistSqr)
|
||||
{
|
||||
return dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character c in CharacterList)
|
||||
{
|
||||
if (c != this && c.IsRemotePlayer)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(c.WorldPosition, WorldPosition);
|
||||
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
}
|
||||
if (dist < DisableSimplePhysicsDistSqr)
|
||||
{
|
||||
return dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return minDist;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,8 +139,9 @@ namespace Barotrauma
|
||||
Collider.FarseerBody.FixedRotation = false;
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
Collider.LinearVelocity = MainLimb.LinearVelocity;
|
||||
Collider.Enabled = false;
|
||||
Collider.FarseerBody.FixedRotation = false;
|
||||
Collider.LinearVelocity = MainLimb.LinearVelocity;
|
||||
Collider.SetTransformIgnoreContacts(MainLimb.SimPosition, MainLimb.Rotation);
|
||||
}
|
||||
if (character.IsDead && deathAnimTimer < deathAnimDuration)
|
||||
|
||||
@@ -286,7 +286,7 @@ namespace Barotrauma
|
||||
var waistJoint = GetJointBetweenLimbs(LimbType.Waist, upperLegType);
|
||||
Vector2 localAnchorWaist = Vector2.Zero;
|
||||
Vector2 localAnchorKnee = Vector2.Zero;
|
||||
if (shoulder != null)
|
||||
if (waistJoint != null)
|
||||
{
|
||||
localAnchorWaist = waistJoint.LimbA.type == upperLegType ? waistJoint.LocalAnchorA : waistJoint.LocalAnchorB;
|
||||
}
|
||||
@@ -298,6 +298,7 @@ namespace Barotrauma
|
||||
upperLegLength = Vector2.Distance(localAnchorWaist, localAnchorKnee);
|
||||
|
||||
LimbJoint ankleJoint = GetJointBetweenLimbs(lowerLegType, footType);
|
||||
if (ankleJoint == null || kneeJoint == null) { return; }
|
||||
lowerLegLength = Vector2.Distance(
|
||||
kneeJoint.LimbA.type == lowerLegType ? kneeJoint.LocalAnchorA : kneeJoint.LocalAnchorB,
|
||||
ankleJoint.LimbA.type == lowerLegType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB);
|
||||
@@ -537,14 +538,13 @@ namespace Barotrauma
|
||||
|
||||
float limpAmount =
|
||||
character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) +
|
||||
character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
|
||||
character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true) +
|
||||
character.CharacterHealth.GetAfflictionStrength("spaceherpes");
|
||||
limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f);
|
||||
|
||||
float walkCycleMultiplier = 1.0f;
|
||||
if (Stairs != null)
|
||||
{
|
||||
//TODO: allow editing these values in character editor?
|
||||
bool running = Math.Abs(targetMovement.X) > 2.0f;
|
||||
TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.7f, 1.7f), TargetMovement.Y);
|
||||
walkCycleMultiplier *= 1.5f;
|
||||
}
|
||||
@@ -579,7 +579,7 @@ namespace Barotrauma
|
||||
if (limpAmount > 0.0f)
|
||||
{
|
||||
//make the footpos oscillate when limping
|
||||
footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f));
|
||||
footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f)) * Dir;
|
||||
}
|
||||
|
||||
movement = overrideTargetMovement == Vector2.Zero ?
|
||||
@@ -663,7 +663,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (TorsoAngle.HasValue) torso.body.SmoothRotate(TorsoAngle.Value * Dir, 50.0f);
|
||||
if (TorsoAngle.HasValue)
|
||||
{
|
||||
float torsoAngle = TorsoAngle.Value;
|
||||
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
|
||||
torsoAngle -= herpesStrength / 150.0f;
|
||||
torso.body.SmoothRotate(torsoAngle * Dir, 50.0f);
|
||||
}
|
||||
if (HeadAngle.HasValue) head.body.SmoothRotate(HeadAngle.Value * Dir, 50.0f);
|
||||
|
||||
if (!onGround)
|
||||
@@ -689,7 +695,6 @@ namespace Barotrauma
|
||||
for (int i = -1; i < 2; i += 2)
|
||||
{
|
||||
Limb foot = i == -1 ? leftFoot : rightFoot;
|
||||
Limb leg = i == -1 ? leftLeg : rightLeg;
|
||||
|
||||
Vector2 footPos = stepSize * -i;
|
||||
footPos += new Vector2(Math.Sign(movement.X) * FootMoveOffset.X, FootMoveOffset.Y);
|
||||
@@ -706,6 +711,15 @@ namespace Barotrauma
|
||||
}
|
||||
footPos.Y = Math.Min(waistPos.Y - colliderPos.Y - 0.4f, footPos.Y);
|
||||
|
||||
#if CLIENT
|
||||
if ((i == 1 && Math.Sign(Math.Sin(WalkPos)) > 0 && Math.Sign(walkPosY) < 0) ||
|
||||
(i == -1 && Math.Sign(Math.Sin(WalkPos)) < 0 && Math.Sign(walkPosY) > 0))
|
||||
{
|
||||
PlayImpactSound(foot);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (!foot.Disabled)
|
||||
{
|
||||
foot.DebugRefPos = colliderPos;
|
||||
@@ -766,7 +780,6 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var foot = i == -1 ? rightFoot : leftFoot;
|
||||
Limb leg = i == -1 ? rightLeg : leftLeg;
|
||||
|
||||
if (!foot.Disabled)
|
||||
{
|
||||
|
||||
+3
-3
@@ -96,16 +96,16 @@ namespace Barotrauma
|
||||
public virtual AnimationType AnimationType { get; protected set; }
|
||||
|
||||
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType.ToString()}";
|
||||
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Animations/";
|
||||
public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null) =>
|
||||
$"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName, animType)}.xml";
|
||||
|
||||
public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
|
||||
{
|
||||
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName, contentPackage))?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
|
||||
string configFilePath = Character.GetConfigFile(speciesName, contentPackage);
|
||||
var folder = XMLExtensions.TryLoadXml(configFilePath)?.Root?.Element("animations")?.GetAttributeString("folder", string.Empty);
|
||||
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
|
||||
{
|
||||
folder = GetDefaultFolder(speciesName);
|
||||
folder = Path.Combine(Path.GetDirectoryName(configFilePath), "Animations");
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
+3
-4
@@ -66,7 +66,6 @@ namespace Barotrauma
|
||||
.Concat(Joints.Select(j => j as RagdollSubParams)));
|
||||
|
||||
public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll";
|
||||
public static string GetDefaultFolder(string speciesName) => $"Content/Characters/{speciesName.CapitaliseFirstInvariant()}/Ragdolls/";
|
||||
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null) => $"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName)}.xml";
|
||||
|
||||
private static readonly object[] dummyParams = new object[]
|
||||
@@ -84,11 +83,11 @@ namespace Barotrauma
|
||||
|
||||
public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
|
||||
{
|
||||
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName, contentPackage))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
|
||||
string configFilePath = Character.GetConfigFile(speciesName, contentPackage);
|
||||
var folder = XMLExtensions.TryLoadXml(configFilePath)?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
|
||||
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
|
||||
{
|
||||
//DebugConsole.NewMessage("[RagollParams] Using the default folder.");
|
||||
folder = GetDefaultFolder(speciesName);
|
||||
folder = Path.Combine(Path.GetDirectoryName(configFilePath), "Ragdolls") + Path.DirectorySeparatorChar;
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
@@ -1047,11 +1047,16 @@ namespace Barotrauma
|
||||
|
||||
protected bool levitatingCollider = true;
|
||||
|
||||
/// <summary>
|
||||
/// How long has the ragdoll stayed motionless
|
||||
/// </summary>
|
||||
private float bodyInRestTimer;
|
||||
|
||||
public bool forceStanding;
|
||||
|
||||
public void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!character.Enabled || Frozen || Invalid) return;
|
||||
if (!character.Enabled || Frozen || Invalid) { return; }
|
||||
|
||||
CheckValidity();
|
||||
|
||||
@@ -1063,6 +1068,8 @@ namespace Barotrauma
|
||||
|
||||
FindHull();
|
||||
PreventOutsideCollision();
|
||||
|
||||
CheckBodyInRest(deltaTime);
|
||||
|
||||
splashSoundTimer -= deltaTime;
|
||||
|
||||
@@ -1315,6 +1322,29 @@ namespace Barotrauma
|
||||
UpdateProjSpecific(deltaTime);
|
||||
}
|
||||
|
||||
private void CheckBodyInRest(float deltaTime)
|
||||
{
|
||||
if (Collider.LinearVelocity.LengthSquared() > 0.01f || character.SelectedBy != null || !character.IsDead)
|
||||
{
|
||||
bodyInRestTimer = 0.0f;
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
limb.body.PhysEnabled = true;
|
||||
}
|
||||
}
|
||||
else if (Limbs.All(l => l != null && !l.body.Enabled || l.LinearVelocity.LengthSquared() < 0.001f))
|
||||
{
|
||||
bodyInRestTimer += deltaTime;
|
||||
if (bodyInRestTimer > 1.0f)
|
||||
{
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
limb.body.PhysEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Invalid { get; private set; }
|
||||
private int validityResets;
|
||||
private bool CheckValidity()
|
||||
|
||||
@@ -298,7 +298,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength");
|
||||
Afflictions.Add(afflictionPrefab.Instantiate(afflictionStrength));
|
||||
var affliction = afflictionPrefab.Instantiate(afflictionStrength);
|
||||
affliction.ApplyProbability = subElement.GetAttributeFloat("probability", 1.0f);
|
||||
Afflictions.Add(affliction);
|
||||
|
||||
break;
|
||||
case "conditional":
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
|
||||
@@ -743,31 +743,33 @@ namespace Barotrauma
|
||||
PressureProtection = 100.0f;
|
||||
}
|
||||
|
||||
List<XElement> inventoryElements = new List<XElement>();
|
||||
List<float> inventoryCommonness = new List<float>();
|
||||
List<XElement> healthElements = new List<XElement>();
|
||||
List<float> healthCommonness = new List<float>();
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "inventory":
|
||||
Inventory = new CharacterInventory(subElement, this);
|
||||
inventoryElements.Add(subElement);
|
||||
inventoryCommonness.Add(subElement.GetAttributeFloat("commonness", 1.0f));
|
||||
break;
|
||||
case "health":
|
||||
CharacterHealth = new CharacterHealth(subElement, this);
|
||||
healthElements.Add(subElement);
|
||||
healthCommonness.Add(subElement.GetAttributeFloat("commonness", 1.0f));
|
||||
break;
|
||||
case "statuseffect":
|
||||
statusEffects.Add(StatusEffect.Load(subElement, Name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<XElement> healthElements = new List<XElement>();
|
||||
List<float> healthCommonness = new List<float>();
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
if (inventoryElements.Count > 0)
|
||||
{
|
||||
if (element.Name.ToString().ToLowerInvariant() != "health") continue;
|
||||
healthElements.Add(element);
|
||||
healthCommonness.Add(element.GetAttributeFloat("commonness", 1.0f));
|
||||
Inventory = new CharacterInventory(
|
||||
inventoryElements.Count == 1 ? inventoryElements[0] : ToolBox.SelectWeightedRandom(inventoryElements, inventoryCommonness, random),
|
||||
this);
|
||||
}
|
||||
|
||||
if (healthElements.Count == 0)
|
||||
{
|
||||
CharacterHealth = new CharacterHealth(this);
|
||||
@@ -834,6 +836,7 @@ namespace Barotrauma
|
||||
|
||||
#if CLIENT
|
||||
head.LoadHuskSprite();
|
||||
head.LoadHerpesSprite();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -977,7 +980,30 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (inputType == InputType.Up || inputType == InputType.Down ||
|
||||
inputType == InputType.Left || inputType == InputType.Right)
|
||||
{
|
||||
var invertControls = CharacterHealth.GetAffliction("invertcontrols");
|
||||
if (invertControls != null)
|
||||
{
|
||||
switch (inputType)
|
||||
{
|
||||
case InputType.Left:
|
||||
inputType = InputType.Right;
|
||||
break;
|
||||
case InputType.Right:
|
||||
inputType = InputType.Left;
|
||||
break;
|
||||
case InputType.Up:
|
||||
inputType = InputType.Down;
|
||||
break;
|
||||
case InputType.Down:
|
||||
inputType = InputType.Up;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys[(int)inputType].Held;
|
||||
}
|
||||
|
||||
@@ -1579,9 +1605,16 @@ namespace Barotrauma
|
||||
//locked wires are never interactable
|
||||
if (wire.Locked) return false;
|
||||
|
||||
//wires are interactable if the character has selected either of the items the wire is connected to
|
||||
if (wire.Connections[0]?.Item != null && SelectedConstruction == wire.Connections[0].Item) return true;
|
||||
if (wire.Connections[1]?.Item != null && SelectedConstruction == wire.Connections[1].Item) return true;
|
||||
//wires are interactable if the character has selected an item the wire is connected to,
|
||||
//and it's disconnected from the other end
|
||||
if (wire.Connections[0]?.Item != null && SelectedConstruction == wire.Connections[0].Item)
|
||||
{
|
||||
return wire.Connections[1] == null;
|
||||
}
|
||||
if (wire.Connections[1]?.Item != null && SelectedConstruction == wire.Connections[1].Item)
|
||||
{
|
||||
return wire.Connections[0] == null;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkLinked && item.DisplaySideBySideWhenLinked)
|
||||
@@ -1891,15 +1924,30 @@ namespace Barotrauma
|
||||
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(otherCharacter.WorldPosition, c.WorldPosition));
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
for (int i = 0; i < GameMain.Server.ConnectedClients.Count; i++)
|
||||
{
|
||||
var spectatePos = GameMain.Server.ConnectedClients[i].SpectatePos;
|
||||
if (spectatePos != null)
|
||||
{
|
||||
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(spectatePos.Value, c.WorldPosition));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (distSqr > NetConfig.DisableCharacterDistSqr)
|
||||
{
|
||||
c.Enabled = false;
|
||||
if (c.IsDead && c.AIController is EnemyAIController)
|
||||
{
|
||||
Entity.Spawner?.AddToRemoveQueue(c);
|
||||
}
|
||||
}
|
||||
else if (distSqr < NetConfig.EnableCharacterDistSqr)
|
||||
{
|
||||
c.Enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Submarine.MainSub != null)
|
||||
{
|
||||
@@ -1907,19 +1955,22 @@ namespace Barotrauma
|
||||
float distSqr = Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition);
|
||||
if (Controlled != null)
|
||||
{
|
||||
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(Controlled.WorldPosition, c.WorldPosition));
|
||||
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(GameMain.GameScreen.Cam.GetPosition(), c.WorldPosition));
|
||||
}
|
||||
|
||||
|
||||
if (distSqr > NetConfig.DisableCharacterDistSqr)
|
||||
{
|
||||
c.Enabled = false;
|
||||
if (c.IsDead && c.AIController is EnemyAIController)
|
||||
{
|
||||
Entity.Spawner?.AddToRemoveQueue(c);
|
||||
}
|
||||
}
|
||||
else if ( distSqr < NetConfig.EnableCharacterDistSqr)
|
||||
else if (distSqr < NetConfig.EnableCharacterDistSqr)
|
||||
{
|
||||
c.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2261,8 +2312,6 @@ namespace Barotrauma
|
||||
speechBubbleColor = color;
|
||||
}
|
||||
|
||||
partial void AdjustKarma(Character attacker, AttackResult attackResult);
|
||||
|
||||
partial void DamageHUD(float amount);
|
||||
|
||||
public void SetAllDamage(float damageAmount, float bleedingDamageAmount, float burnDamageAmount)
|
||||
@@ -2358,7 +2407,12 @@ namespace Barotrauma
|
||||
{
|
||||
hitLimb = null;
|
||||
|
||||
if (Removed) return new AttackResult();
|
||||
if (Removed) { return new AttackResult(); }
|
||||
|
||||
if (attacker != null && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
{
|
||||
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
||||
}
|
||||
|
||||
float closestDistance = 0.0f;
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
@@ -2376,7 +2430,12 @@ namespace Barotrauma
|
||||
|
||||
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, List<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null)
|
||||
{
|
||||
if (Removed) return new AttackResult();
|
||||
if (Removed) { return new AttackResult(); }
|
||||
|
||||
if (attacker != null && attacker != this && GameMain.NetworkMember != null && !GameMain.NetworkMember.ServerSettings.AllowFriendlyFire)
|
||||
{
|
||||
if (attacker.TeamID == TeamID) { return new AttackResult(); }
|
||||
}
|
||||
|
||||
SetStun(stun);
|
||||
Vector2 dir = hitLimb.WorldPosition - worldPosition;
|
||||
@@ -2395,7 +2454,6 @@ namespace Barotrauma
|
||||
OnAttacked?.Invoke(attacker, attackResult);
|
||||
OnAttackedProjSpecific(attacker, attackResult);
|
||||
};
|
||||
AdjustKarma(attacker, attackResult);
|
||||
|
||||
if (attacker != null && attackResult.Damage > 0.0f)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,11 @@ namespace Barotrauma
|
||||
public float StrengthDiminishMultiplier = 1.0f;
|
||||
public Affliction MultiplierSource;
|
||||
|
||||
/// <summary>
|
||||
/// Probability for the affliction to be applied. Used by attacks.
|
||||
/// </summary>
|
||||
public float ApplyProbability;
|
||||
|
||||
/// <summary>
|
||||
/// Which character gave this affliction
|
||||
/// </summary>
|
||||
@@ -162,6 +167,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (StatusEffect statusEffect in currentEffect.StatusEffects)
|
||||
{
|
||||
statusEffect.SetUser(Source);
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
statusEffect.Apply(ActionType.OnActive, deltaTime, characterHealth.Character, characterHealth.Character);
|
||||
|
||||
@@ -157,6 +157,9 @@ namespace Barotrauma
|
||||
//how high the strength has to be for the affliction icon to be shown with a health scanner
|
||||
public readonly float ShowInHealthScannerThreshold = 0.05f;
|
||||
|
||||
//how much karma changes when a player applies this affliction to someone (per strength of the affliction)
|
||||
public float KarmaChangeOnApplied;
|
||||
|
||||
public float BurnOverlayAlpha;
|
||||
public float DamageOverlayAlpha;
|
||||
|
||||
@@ -265,9 +268,12 @@ namespace Barotrauma
|
||||
DamageOverlayAlpha = element.GetAttributeFloat("damageoverlayalpha", 0.0f);
|
||||
BurnOverlayAlpha = element.GetAttributeFloat("burnoverlayalpha", 0.0f);
|
||||
|
||||
KarmaChangeOnApplied = element.GetAttributeFloat("karmachangeonapplied", 0.0f);
|
||||
|
||||
CauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeath." + Identifier, true) ?? element.GetAttributeString("causeofdeathdescription", "");
|
||||
SelfCauseOfDeathDescription = TextManager.Get("AfflictionCauseOfDeathSelf." + Identifier, true) ?? element.GetAttributeString("selfcauseofdeathdescription", "");
|
||||
|
||||
|
||||
AchievementOnRemoved = element.GetAttributeString("achievementonremoved", "");
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class AfflictionSpaceHerpes : Affliction
|
||||
{
|
||||
private float invertControlsCooldown = 60.0f;
|
||||
private float stunCoolDown = 60.0f;
|
||||
public AfflictionSpaceHerpes(AfflictionPrefab prefab, float strength) : base(prefab, strength)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Update(CharacterHealth characterHealth, Limb targetLimb, float deltaTime)
|
||||
{
|
||||
base.Update(characterHealth, targetLimb, deltaTime);
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
invertControlsCooldown -= deltaTime;
|
||||
if (invertControlsCooldown <= 0.0f)
|
||||
{
|
||||
//invert controls every 126-234 seconds when strength is close to 0
|
||||
//every 56-104 seconds when strength is close to 100
|
||||
invertControlsCooldown = (180.0f - Strength) * Rand.Range(0.7f, 1.3f);
|
||||
var invertControlsAffliction = AfflictionPrefab.List.Find(ap => ap.Identifier == "invertcontrols");
|
||||
float invertControlsDuration = MathHelper.Lerp(10.0f, 60.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
|
||||
characterHealth.ApplyAffliction(null, new Affliction(invertControlsAffliction, invertControlsDuration));
|
||||
}
|
||||
|
||||
if (Strength > 50.0f)
|
||||
{
|
||||
stunCoolDown -= deltaTime;
|
||||
if (stunCoolDown <= 0.0f)
|
||||
{
|
||||
//stun every 126-234 seconds when strength is close to 0
|
||||
//stun 56-104 seconds when strength is close to 100
|
||||
stunCoolDown = (180.0f - Strength) * Rand.Range(0.7f, 1.3f);
|
||||
float stunDuration = MathHelper.Lerp(3.0f, 10.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
|
||||
characterHealth.Character.SetStun(stunDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,7 +411,7 @@ namespace Barotrauma
|
||||
amount -= reduceAmount;
|
||||
}
|
||||
}
|
||||
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
public void ApplyDamage(Limb hitLimb, AttackResult attackResult)
|
||||
|
||||
@@ -407,7 +407,7 @@ namespace Barotrauma
|
||||
{
|
||||
List<DamageModifier> appliedDamageModifiers = new List<DamageModifier>();
|
||||
//create a copy of the original affliction list to prevent modifying the afflictions of an Attack/StatusEffect etc
|
||||
afflictions = new List<Affliction>(afflictions);
|
||||
afflictions = new List<Affliction>(afflictions.Where(a => Rand.Range(0.0f, 1.0f) <= a.ApplyProbability));
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
foreach (DamageModifier damageModifier in damageModifiers)
|
||||
|
||||
@@ -485,6 +485,22 @@ namespace Barotrauma
|
||||
List.Add(package);
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(Path);
|
||||
GameMain.Config.SelectedContentPackages.Remove(this);
|
||||
GameMain.Config.SaveNewPlayerConfig();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to delete content package \"" + Name + "\".", e);
|
||||
return;
|
||||
}
|
||||
List.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class ContentFile
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace Barotrauma
|
||||
if (!handle.Coroutine.MoveNext()) return;
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException tae)
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
//not an error, don't worry about it
|
||||
}
|
||||
|
||||
@@ -110,7 +110,15 @@ namespace Barotrauma
|
||||
|
||||
private static void AssignOnExecute(string names, Action<string[]> onExecute)
|
||||
{
|
||||
commands.First(c => c.names.Intersect(names.Split('|')).Count() > 0).OnExecute = onExecute;
|
||||
var matchingCommand = commands.Find(c => c.names.Intersect(names.Split('|')).Count() > 0);
|
||||
if (matchingCommand == null)
|
||||
{
|
||||
throw new Exception("AssignOnExecute failed. Command matching the name(s) \""+names+"\" not found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
matchingCommand.OnExecute = onExecute;
|
||||
}
|
||||
}
|
||||
|
||||
static DebugConsole()
|
||||
@@ -251,16 +259,11 @@ namespace Barotrauma
|
||||
spawnPosParams.ToArray()
|
||||
};
|
||||
}, isCheat: true));
|
||||
|
||||
|
||||
|
||||
commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
|
||||
{
|
||||
HumanAIController.DisableCrewAI = true;
|
||||
NewMessage("Crew AI disabled", Color.Red);
|
||||
// This is probably not where it should be?
|
||||
//ThrowError("Karma has not been fully implemented yet, and is disabled in this version of Barotrauma.");
|
||||
/*if (GameMain.Server == null) return;
|
||||
GameMain.Server.KarmaEnabled = !GameMain.Server.KarmaEnabled;*/
|
||||
}));
|
||||
|
||||
commands.Add(new Command("enablecrewai", "enablecrewai: Enable the AI of the NPCs in the crew.", (string[] args) =>
|
||||
@@ -304,8 +307,31 @@ namespace Barotrauma
|
||||
commands.Add(new Command("revokecommandperm", "revokecommandperm [id]: Revokes permission to use the specified console commands from the player with the specified client ID.", null));
|
||||
|
||||
commands.Add(new Command("showperm", "showperm [id]: Shows the current administrative permissions of the client with the specified client ID.", null));
|
||||
|
||||
commands.Add(new Command("respawnnow", "respawnnow: Trigger a respawn immediately if there are any clients waiting to respawn.", null));
|
||||
|
||||
//commands.Add(new Command("togglekarma", "togglekarma: Toggles the karma system.", null));
|
||||
commands.Add(new Command("showkarma", "showkarma: Show the current karma values of the players.", null));
|
||||
commands.Add(new Command("togglekarma", "togglekarma: Toggle the karma system on/off.", null));
|
||||
commands.Add(new Command("resetkarma", "resetkarma [client]: Resets the karma value of the specified client to 100.", null,
|
||||
() =>
|
||||
{
|
||||
if (GameMain.NetworkMember?.ConnectedClients == null) { return null; }
|
||||
return new string[][]
|
||||
{
|
||||
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray()
|
||||
};
|
||||
}));
|
||||
commands.Add(new Command("setkarma", "setkarma [client] [0-100]: Sets the karma of the specified client to the specified value.", null,
|
||||
() =>
|
||||
{
|
||||
if (GameMain.NetworkMember?.ConnectedClients == null) { return null; }
|
||||
return new string[][]
|
||||
{
|
||||
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
|
||||
new string[] { "50" }
|
||||
};
|
||||
}));
|
||||
commands.Add(new Command("togglekarmatestmode", "togglekarmatestmode: Toggle the karma test mode on/off. When test mode is enabled, clients get notified when their karma value changes (including the reason for the increase/decrease) and the server doesn't ban clients whose karma decreases below the ban threshold.", null));
|
||||
|
||||
commands.Add(new Command("kick", "kick [name]: Kick a player out of the server.", (string[] args) =>
|
||||
{
|
||||
@@ -654,11 +680,11 @@ namespace Barotrauma
|
||||
|
||||
if (args.Length > 0 && args[0].ToLowerInvariant() == "start")
|
||||
{
|
||||
Submarine.MainSub.SetPosition(Level.Loaded.StartPosition);
|
||||
Submarine.MainSub.SetPosition(Level.Loaded.StartPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
Submarine.MainSub.SetPosition(Level.Loaded.EndPosition);
|
||||
Submarine.MainSub.SetPosition(Level.Loaded.EndPosition - Vector2.UnitY * Submarine.MainSub.Borders.Height);
|
||||
}
|
||||
}, isCheat: true));
|
||||
|
||||
|
||||
@@ -280,16 +280,23 @@ namespace Barotrauma
|
||||
|
||||
float holeCount = 0.0f;
|
||||
floodingAmount = 0.0f;
|
||||
int hullCount = 0;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine == null || hull.Submarine.IsOutpost) { continue; }
|
||||
hullCount++;
|
||||
foreach (Gap gap in hull.ConnectedGaps)
|
||||
{
|
||||
if (!gap.IsRoomToRoom) holeCount += gap.Open;
|
||||
}
|
||||
floodingAmount += hull.WaterVolume / hull.Volume / Hull.hullList.Count;
|
||||
floodingAmount += hull.WaterVolume / hull.Volume;
|
||||
fireAmount += hull.FireSources.Sum(fs => fs.Size.X);
|
||||
}
|
||||
if (hullCount > 0)
|
||||
{
|
||||
floodingAmount = floodingAmount / hullCount;
|
||||
}
|
||||
|
||||
//hull integrity at 0.0 if there are 10 or more wide-open holes
|
||||
avgHullIntegrity = MathHelper.Clamp(1.0f - holeCount / 10.0f, 0.0f, 1.0f);
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -12,9 +8,6 @@ namespace Barotrauma
|
||||
private Submarine[] subs;
|
||||
private List<Character>[] crews;
|
||||
|
||||
private bool initialized = false;
|
||||
private int state = 0;
|
||||
|
||||
private string[] descriptions;
|
||||
private static string[] teamNames = { "Team A", "Team B" };
|
||||
|
||||
|
||||
@@ -81,10 +81,8 @@ namespace Barotrauma
|
||||
|
||||
bool success =
|
||||
GameMain.Server.ConnectedClients.Any(c => c.InGame && c.Character != null && !c.Character.IsDead);
|
||||
|
||||
#if CLIENT
|
||||
|
||||
success = success || (GameMain.Server.Character != null && !GameMain.Server.Character.IsDead);
|
||||
#endif
|
||||
|
||||
/*if (success)
|
||||
{
|
||||
@@ -119,11 +117,7 @@ namespace Barotrauma
|
||||
{
|
||||
c.Inventory?.DeleteAllItems();
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession.CrewManager.EndRound();
|
||||
#endif
|
||||
|
||||
|
||||
if (success)
|
||||
{
|
||||
bool atEndPosition = Submarine.MainSub.AtEndPosition;
|
||||
|
||||
@@ -110,7 +110,25 @@ namespace Barotrauma
|
||||
public override bool CanBePut(Item item, int i)
|
||||
{
|
||||
return base.CanBePut(item, i) && item.AllowedSlots.Contains(SlotTypes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there is no room in the generic inventory (InvSlotType.Any), check if the item can be auto-equipped into its respective limbslot
|
||||
/// </summary>
|
||||
public bool TryPutItemWithAutoEquipCheck(Item item, Character user, List<InvSlotType> allowedSlots = null, bool createNetworkEvent = true)
|
||||
{
|
||||
// Does not auto-equip the item if specified and no suitable any slot found (for example handcuffs are not auto-equipped)
|
||||
if (item.AllowedSlots.Contains(InvSlotType.Any))
|
||||
{
|
||||
var wearable = item.GetComponent<Wearable>();
|
||||
if (wearable != null && !wearable.AutoEquipWhenFull && CheckIfAnySlotAvailable(item, false) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return TryPutItem(item, user, allowedSlots, createNetworkEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there is room, puts the item in the inventory and returns true, otherwise returns false
|
||||
@@ -139,29 +157,10 @@ namespace Barotrauma
|
||||
//try to place the item in a LimbSlot.Any slot if that's allowed
|
||||
if (allowedSlots.Contains(InvSlotType.Any))
|
||||
{
|
||||
for (int i = 0; i < capacity; i++)
|
||||
int freeIndex = CheckIfAnySlotAvailable(item, inWrongSlot);
|
||||
if (freeIndex > -1)
|
||||
{
|
||||
if (SlotTypes[i] != InvSlotType.Any) continue;
|
||||
if (Items[i] == item)
|
||||
{
|
||||
PutItem(item, i, user, true, createNetworkEvent);
|
||||
item.Unequip(character);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (SlotTypes[i] != InvSlotType.Any) continue;
|
||||
if (inWrongSlot)
|
||||
{
|
||||
if (Items[i] != item && Items[i] != null) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Items[i] != null) continue;
|
||||
}
|
||||
|
||||
PutItem(item, i, user, true, createNetworkEvent);
|
||||
PutItem(item, freeIndex, user, true, createNetworkEvent);
|
||||
item.Unequip(character);
|
||||
return true;
|
||||
}
|
||||
@@ -242,10 +241,37 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return placedInSlot > -1;
|
||||
}
|
||||
|
||||
public int CheckIfAnySlotAvailable(Item item, bool inWrongSlot)
|
||||
{
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (SlotTypes[i] != InvSlotType.Any) continue;
|
||||
if (Items[i] == item)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (SlotTypes[i] != InvSlotType.Any) continue;
|
||||
if (inWrongSlot)
|
||||
{
|
||||
if (Items[i] != item && Items[i] != null) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Items[i] != null) continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override bool TryPutItem(Item item, int index, bool allowSwapping, bool allowCombine, Character user, bool createNetworkEvent = true)
|
||||
{
|
||||
if (index < 0 || index >= Items.Length)
|
||||
|
||||
@@ -312,6 +312,30 @@ namespace Barotrauma.Items.Components
|
||||
joint.CollideConnected = true;
|
||||
}
|
||||
|
||||
public int GetDir()
|
||||
{
|
||||
if (DockingDir != 0) { return DockingDir; }
|
||||
|
||||
if (door != null)
|
||||
{
|
||||
if (door.LinkedGap.linkedTo.Count == 1)
|
||||
{
|
||||
return IsHorizontal ?
|
||||
Math.Sign(door.Item.WorldPosition.X - door.LinkedGap.linkedTo[0].WorldPosition.X) :
|
||||
Math.Sign(door.Item.WorldPosition.Y - door.LinkedGap.linkedTo[0].WorldPosition.Y);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
return IsHorizontal ?
|
||||
Math.Sign(item.WorldPosition.X - item.Submarine.WorldPosition.X) :
|
||||
Math.Sign(item.WorldPosition.Y - item.Submarine.WorldPosition.Y);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void ConnectWireBetweenPorts()
|
||||
{
|
||||
Wire wire = item.GetComponent<Wire>();
|
||||
|
||||
@@ -250,17 +250,14 @@ namespace Barotrauma.Items.Components
|
||||
if (item.Condition <= RepairThreshold) { return true; }
|
||||
if (requiredItems.Any() && !hasValidIdCard)
|
||||
{
|
||||
ForceOpen(ActionType.OnPicked);
|
||||
ToggleState(ActionType.OnPicked);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ForceOpen(ActionType actionType)
|
||||
private void ToggleState(ActionType actionType)
|
||||
{
|
||||
SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true); //crowbar function
|
||||
#if CLIENT
|
||||
PlaySound(actionType, item.WorldPosition, picker);
|
||||
#endif
|
||||
SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true, forcedOpen: actionType == ActionType.OnPicked);
|
||||
}
|
||||
|
||||
public override bool Select(Character character)
|
||||
@@ -272,7 +269,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float originalPickingTime = PickingTime;
|
||||
PickingTime = 0;
|
||||
ForceOpen(ActionType.OnUse);
|
||||
ToggleState(ActionType.OnUse);
|
||||
PickingTime = originalPickingTime;
|
||||
}
|
||||
else if (hasRequiredItems)
|
||||
@@ -538,11 +535,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (connection.Name == "toggle")
|
||||
{
|
||||
SetState(!wasOpen, false, true);
|
||||
SetState(!wasOpen, false, true, forcedOpen: false);
|
||||
}
|
||||
else if (connection.Name == "set_state")
|
||||
{
|
||||
SetState(signal != "0", false, true);
|
||||
SetState(signal != "0", false, true, forcedOpen: false);
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
@@ -555,9 +552,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void TrySetState(bool open, bool isNetworkMessage, bool sendNetworkMessage = false)
|
||||
{
|
||||
SetState(open, isNetworkMessage, sendNetworkMessage);
|
||||
SetState(open, isNetworkMessage, sendNetworkMessage, forcedOpen: false);
|
||||
}
|
||||
|
||||
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage);
|
||||
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage, bool forcedOpen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,6 +558,30 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
if (!attachable)
|
||||
{
|
||||
return base.Save(parentElement);
|
||||
}
|
||||
|
||||
var tempMsg = DisplayMsg;
|
||||
var tempPickKey = PickKey;
|
||||
var tempRequiredItems = requiredItems;
|
||||
|
||||
DisplayMsg = prevMsg;
|
||||
PickKey = prevPickKey;
|
||||
requiredItems = prevRequiredItems;
|
||||
|
||||
XElement saveElement = base.Save(parentElement);
|
||||
|
||||
DisplayMsg = tempMsg;
|
||||
PickKey = tempPickKey;
|
||||
requiredItems = tempRequiredItems;
|
||||
|
||||
return saveElement;
|
||||
}
|
||||
|
||||
public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
||||
{
|
||||
base.ServerWrite(msg, c, extraData);
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Character activePicker;
|
||||
|
||||
private CoroutineHandle pickingCoroutine;
|
||||
|
||||
public List<InvSlotType> AllowedSlots
|
||||
{
|
||||
get { return allowedSlots; }
|
||||
@@ -69,7 +71,7 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
CoroutineManager.StartCoroutine(WaitForPick(picker, PickingTime));
|
||||
pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, PickingTime));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -81,7 +83,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual bool OnPicked(Character picker)
|
||||
{
|
||||
if (picker.Inventory.TryPutItem(item, picker, allowedSlots))
|
||||
if (picker.Inventory.TryPutItemWithAutoEquipCheck(item, picker, allowedSlots))
|
||||
{
|
||||
if (!picker.HasSelectedItem(item) && item.body != null) item.body.Enabled = false;
|
||||
this.picker = picker;
|
||||
@@ -136,7 +138,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
picker.UpdateHUDProgressBar(
|
||||
Character.Controlled?.UpdateHUDProgressBar(
|
||||
this,
|
||||
item.WorldPosition,
|
||||
pickTimer / requiredTime,
|
||||
@@ -160,13 +162,18 @@ namespace Barotrauma.Items.Components
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private void StopPicking(Character picker)
|
||||
protected void StopPicking(Character picker)
|
||||
{
|
||||
if (picker != null)
|
||||
{
|
||||
picker.AnimController.Anim = AnimController.Animation.None;
|
||||
picker.PickingItem = null;
|
||||
}
|
||||
if (pickingCoroutine != null)
|
||||
{
|
||||
CoroutineManager.StopCoroutines(pickingCoroutine);
|
||||
pickingCoroutine = null;
|
||||
}
|
||||
activePicker = null;
|
||||
pickTimer = 0.0f;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
|
||||
Vector2 barrelPos = TransformedBarrelPos;
|
||||
//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(sourcePos, barrelPos, projectile.IgnoredBodies) == null)
|
||||
if (Submarine.PickBody(sourcePos, barrelPos, projectile.IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
|
||||
{
|
||||
//no obstacles -> we can spawn the projectile at the barrel
|
||||
projectilePos = barrelPos;
|
||||
|
||||
@@ -14,12 +14,23 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class RepairTool : ItemComponent
|
||||
{
|
||||
public enum UseEnvironment
|
||||
{
|
||||
Air, Water, Both, None
|
||||
};
|
||||
|
||||
private readonly List<string> fixableEntities;
|
||||
private Vector2 pickedPosition;
|
||||
private float activeTimer;
|
||||
|
||||
private Vector2 debugRayStartPos, debugRayEndPos;
|
||||
|
||||
|
||||
[Serialize("Both", false)]
|
||||
public UseEnvironment UsableIn
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float Range { get; set; }
|
||||
|
||||
@@ -43,6 +54,9 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, false)]
|
||||
public bool RepairMultiple { get; set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float FireProbability { get; set; }
|
||||
|
||||
public Vector2 TransformedBarrelPos
|
||||
{
|
||||
get
|
||||
@@ -109,6 +123,29 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UsableIn == UseEnvironment.None)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnFailure, deltaTime, character);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
if (UsableIn == UseEnvironment.Air)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnFailure, deltaTime, character);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UsableIn == UseEnvironment.Water)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnFailure, deltaTime, character);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 targetPosition = item.WorldPosition;
|
||||
targetPosition += new Vector2(
|
||||
(float)Math.Cos(item.body.Rotation),
|
||||
@@ -149,7 +186,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Repair(rayStart - character.Submarine.SimPosition, rayEnd - character.Submarine.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
}
|
||||
|
||||
|
||||
UseProjSpecific(deltaTime);
|
||||
|
||||
return true;
|
||||
@@ -162,9 +199,12 @@ namespace Barotrauma.Items.Components
|
||||
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies)
|
||||
{
|
||||
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair;
|
||||
|
||||
float lastPickedFraction = 0.0f;
|
||||
if (RepairMultiple)
|
||||
{
|
||||
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true);
|
||||
lastPickedFraction = Submarine.LastPickedFraction;
|
||||
Type lastHitType = null;
|
||||
hitCharacters.Clear();
|
||||
foreach (Body body in bodies)
|
||||
@@ -194,6 +234,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (FixBody(user, deltaTime, degreeOfSuccess, body))
|
||||
{
|
||||
lastPickedFraction = Submarine.LastPickedBodyDist(body);
|
||||
if (bodyType != null) { lastHitType = bodyType; }
|
||||
}
|
||||
}
|
||||
@@ -205,13 +246,14 @@ namespace Barotrauma.Items.Components
|
||||
ignoredBodies, collisionCategories, ignoreSensors: false,
|
||||
customPredicate: (Fixture f) => { return f?.Body?.UserData != null; },
|
||||
allowInsideFixture: true));
|
||||
lastPickedFraction = Submarine.LastPickedFraction;
|
||||
}
|
||||
|
||||
if (ExtinguishAmount > 0.0f && item.CurrentHull != null)
|
||||
{
|
||||
fireSourcesInRange.Clear();
|
||||
//step along the ray in 10% intervals, collecting all fire sources in the range
|
||||
for (float x = 0.0f; x <= Submarine.LastPickedFraction; x += 0.1f)
|
||||
for (float x = 0.0f; x <= lastPickedFraction; x += 0.1f)
|
||||
{
|
||||
Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * x);
|
||||
if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; }
|
||||
@@ -230,6 +272,20 @@ namespace Barotrauma.Items.Components
|
||||
foreach (FireSource fs in fireSourcesInRange)
|
||||
{
|
||||
fs.Extinguish(deltaTime, ExtinguishAmount);
|
||||
#if SERVER
|
||||
GameMain.Server.KarmaManager.OnExtinguishingFire(user, deltaTime);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (Rand.Range(0.0f, 1.0f) < FireProbability * deltaTime)
|
||||
{
|
||||
Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * lastPickedFraction * 0.9f);
|
||||
if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; }
|
||||
new FireSource(displayPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,12 +298,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (targetBody.UserData is Structure targetStructure)
|
||||
{
|
||||
if (!fixableEntities.Contains("structure") && !fixableEntities.Contains(targetStructure.Prefab.Identifier)) { return false; }
|
||||
if (targetStructure.IsPlatform) { return false; }
|
||||
|
||||
int sectionIndex = targetStructure.FindSectionIndex(ConvertUnits.ToDisplayUnits(pickedPosition));
|
||||
if (sectionIndex < 0) { return false; }
|
||||
|
||||
if (!fixableEntities.Contains("structure") && !fixableEntities.Contains(targetStructure.Prefab.Identifier)) { return true; }
|
||||
|
||||
FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
|
||||
targetStructure.AddDamage(sectionIndex, -StructureFixAmount * degreeOfSuccess, user);
|
||||
|
||||
@@ -283,9 +339,7 @@ namespace Barotrauma.Items.Components
|
||||
else if (targetBody.UserData is Item targetItem)
|
||||
{
|
||||
targetItem.IsHighlighted = true;
|
||||
|
||||
float prevCondition = targetItem.Condition;
|
||||
|
||||
|
||||
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, targetItem.AllPropertyObjects);
|
||||
|
||||
var levelResource = targetItem.GetComponent<LevelResource>();
|
||||
@@ -300,9 +354,9 @@ namespace Barotrauma.Items.Components
|
||||
targetItem.WorldPosition,
|
||||
levelResource.DeattachTimer / levelResource.DeattachDuration,
|
||||
Color.Red, Color.Green);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
FixItemProjSpecific(user, deltaTime, targetItem, prevCondition);
|
||||
FixItemProjSpecific(user, deltaTime, targetItem);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -310,13 +364,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void FixStructureProjSpecific(Character user, float deltaTime, Structure targetStructure, int sectionIndex);
|
||||
partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter);
|
||||
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, float prevCondition);
|
||||
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem);
|
||||
|
||||
private float sinTime;
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
Gap leak = objective.OperateTarget as Gap;
|
||||
if (leak == null) return true;
|
||||
if (!(objective.OperateTarget is Gap leak)) return true;
|
||||
|
||||
Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
|
||||
float dist = fromItemToLeak.Length();
|
||||
@@ -461,7 +514,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,14 +64,19 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (picker.IsKeyDown(InputType.Aim) && picker.IsKeyHit(InputType.Shoot))
|
||||
throwing = true;
|
||||
if (picker.IsKeyDown(InputType.Aim) && picker.IsKeyHit(InputType.Shoot)) { throwing = true; }
|
||||
if (!picker.IsKeyDown(InputType.Aim) && !throwing) { throwPos = 0.0f; }
|
||||
bool aim = picker.IsKeyDown(InputType.Aim) && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
|
||||
|
||||
if (!picker.IsKeyDown(InputType.Aim) && !throwing) throwPos = 0.0f;
|
||||
if (picker.IsUnconscious || picker.IsDead || !picker.AllowInput)
|
||||
{
|
||||
throwing = false;
|
||||
aim = false;
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
|
||||
|
||||
if (item.body.Dir != picker.AnimController.Dir) Flip();
|
||||
if (item.body.Dir != picker.AnimController.Dir) { Flip(); }
|
||||
|
||||
AnimController ac = picker.AnimController;
|
||||
|
||||
@@ -79,7 +84,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!throwing)
|
||||
{
|
||||
bool aim = picker.IsKeyDown(InputType.Aim) && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
|
||||
if (aim)
|
||||
{
|
||||
throwPos = MathUtils.WrapAnglePi(System.Math.Min(throwPos + deltaTime * 5.0f, MathHelper.PiOver2));
|
||||
@@ -123,7 +127,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, thrower); //Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
|
||||
//Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
|
||||
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, thrower, user: thrower);
|
||||
}
|
||||
throwing = false;
|
||||
}
|
||||
|
||||
@@ -65,20 +65,25 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; protected set; }
|
||||
|
||||
|
||||
public float IsActiveTimer;
|
||||
public virtual bool IsActive
|
||||
{
|
||||
get { return isActive; }
|
||||
set
|
||||
{
|
||||
#if CLIENT
|
||||
if (!value && isActive)
|
||||
if (!value)
|
||||
{
|
||||
StopSounds(ActionType.OnActive);
|
||||
IsActiveTimer = 0.0f;
|
||||
if (isActive)
|
||||
{
|
||||
StopSounds(ActionType.OnActive);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (AITarget != null) AITarget.Enabled = value;
|
||||
isActive = value;
|
||||
isActive = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +389,10 @@ namespace Barotrauma.Items.Components
|
||||
item.Use(1.0f);
|
||||
break;
|
||||
case "toggle":
|
||||
IsActive = !isActive;
|
||||
if (signal != "0")
|
||||
{
|
||||
IsActive = !isActive;
|
||||
}
|
||||
break;
|
||||
case "set_active":
|
||||
case "set_state":
|
||||
@@ -410,8 +418,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (item.ParentInventory != null)
|
||||
{
|
||||
Character owner = (Character)item.ParentInventory.Owner;
|
||||
if (owner != null && owner.HasSelectedItem(item)) item.Unequip(owner);
|
||||
if (item.ParentInventory.Owner is Character owner && owner.HasSelectedItem(item))
|
||||
{
|
||||
item.Unequip(owner);
|
||||
}
|
||||
item.ParentInventory.RemoveItem(item);
|
||||
}
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
@@ -424,8 +434,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (this.Item.ParentInventory != null)
|
||||
{
|
||||
Character owner = (Character)this.Item.ParentInventory.Owner;
|
||||
if (owner != null && owner.HasSelectedItem(this.Item)) this.Item.Unequip(owner);
|
||||
if (this.Item.ParentInventory.Owner is Character owner && owner.HasSelectedItem(this.Item))
|
||||
{
|
||||
this.Item.Unequip(owner);
|
||||
}
|
||||
this.Item.ParentInventory.RemoveItem(this.Item);
|
||||
}
|
||||
Entity.Spawner.AddToRemoveQueue(this.Item);
|
||||
@@ -561,14 +573,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual void FlipY(bool relativeToSub) { }
|
||||
|
||||
public bool HasRequiredContainedItems(bool addMessage, string msg = null)
|
||||
public bool HasRequiredContainedItems(Character user, bool addMessage, string msg = null)
|
||||
{
|
||||
if (!requiredItems.ContainsKey(RelatedItem.RelationType.Contained)) return true;
|
||||
if (item.OwnInventory == null) return false;
|
||||
|
||||
foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Contained])
|
||||
{
|
||||
if (!item.OwnInventory.Items.Any(it => it != null && it.Condition > 0.0f && ri.MatchesItem(it)))
|
||||
if (!ri.CheckRequirements(user, item))
|
||||
{
|
||||
#if CLIENT
|
||||
msg = msg ?? ri.Msg;
|
||||
|
||||
@@ -74,14 +74,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(0.0f, float.MaxValue, ToolTip = "How long the temperature has to stay critical until a meltdown occurs."), Serialize(30.0f, true)]
|
||||
[Editable(0.0f, float.MaxValue, ToolTip = "How long the temperature has to stay critical until a meltdown occurs."), Serialize(120.0f, true)]
|
||||
public float MeltdownDelay
|
||||
{
|
||||
get { return meltDownDelay; }
|
||||
set { meltDownDelay = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
[Editable(0.0f, float.MaxValue, ToolTip = "How long the temperature has to stay critical until the reactor catches fire."), Serialize(10.0f, true)]
|
||||
[Editable(0.0f, float.MaxValue, ToolTip = "How long the temperature has to stay critical until the reactor catches fire."), Serialize(30.0f, true)]
|
||||
public float FireDelay
|
||||
{
|
||||
get { return fireDelay; }
|
||||
@@ -132,6 +132,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, true)]
|
||||
public bool TemperatureCritical
|
||||
{
|
||||
get { return temperature > allowedTemperature.Y; }
|
||||
set { /*do nothing*/ }
|
||||
}
|
||||
|
||||
private float correctTurbineOutput;
|
||||
|
||||
private float targetFissionRate;
|
||||
@@ -384,6 +391,14 @@ namespace Barotrauma.Items.Components
|
||||
float prevFireTimer = fireTimer;
|
||||
fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
|
||||
|
||||
|
||||
#if SERVER
|
||||
if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedConstruction == item)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnReactorOverHeating(blameOnBroken.Character, deltaTime);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fireTimer >= FireDelay && prevFireTimer < fireDelay)
|
||||
{
|
||||
new FireSource(item.WorldPosition);
|
||||
@@ -437,14 +452,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void MeltDown()
|
||||
{
|
||||
if (item.Condition <= 0.0f) return;
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null) return;
|
||||
#endif
|
||||
|
||||
#if SERVER
|
||||
GameServer.Log("Reactor meltdown!", ServerLog.MessageType.ItemInteraction);
|
||||
#endif
|
||||
if (item.Condition <= 0.0f) { return; }
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
item.Condition = 0.0f;
|
||||
fireTimer = 0.0f;
|
||||
@@ -461,9 +470,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (GameMain.Server != null && GameMain.Server.ConnectedClients.Contains(blameOnBroken))
|
||||
GameServer.Log("Reactor meltdown!", ServerLog.MessageType.ItemInteraction);
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
blameOnBroken.Karma = 0.0f;
|
||||
GameMain.Server.KarmaManager.OnReactorMeltdown(blameOnBroken?.Character);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Sonar : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
public enum Mode
|
||||
{
|
||||
Active,
|
||||
Passive
|
||||
};
|
||||
|
||||
public const float DefaultSonarRange = 10000.0f;
|
||||
|
||||
class ConnectedTransducer
|
||||
@@ -35,18 +41,30 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float range;
|
||||
|
||||
private float pingState;
|
||||
private const float PingFrequency = 0.5f;
|
||||
|
||||
private Mode currentMode = Mode.Passive;
|
||||
|
||||
private class ActivePing
|
||||
{
|
||||
public float State;
|
||||
public bool IsDirectional;
|
||||
public Vector2 Direction;
|
||||
public float PrevPingRadius;
|
||||
}
|
||||
// rotating list of currently active pings
|
||||
private ActivePing[] activePings = new ActivePing[8];
|
||||
// total number of currently active pings, range [0, activePings.Length[
|
||||
private int activePingsCount;
|
||||
// currently active ping index on the above list
|
||||
private int currentPingIndex = -1;
|
||||
|
||||
private const float MinZoom = 1.0f, MaxZoom = 4.0f;
|
||||
private float zoom = 1.0f;
|
||||
|
||||
private bool useDirectionalPing = false;
|
||||
private Vector2 lastPingDirection = new Vector2(1.0f, 0.0f);
|
||||
private Vector2 pingDirection = new Vector2(1.0f, 0.0f);
|
||||
|
||||
//was the last ping sent with directional pinging
|
||||
private bool isLastPingDirectional;
|
||||
|
||||
private Sprite pingCircle, directionalPingCircle, screenOverlay, screenBackground;
|
||||
private Sprite sonarBlip;
|
||||
private Sprite lineSprite;
|
||||
@@ -86,24 +104,24 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get { return zoom; }
|
||||
}
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.IsActive;
|
||||
}
|
||||
|
||||
public Mode CurrentMode
|
||||
{
|
||||
get => currentMode;
|
||||
set
|
||||
{
|
||||
base.IsActive = value;
|
||||
if (!value && item.CurrentHull != null)
|
||||
currentMode = value;
|
||||
if (value == Mode.Passive)
|
||||
{
|
||||
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
|
||||
currentPingIndex = -1;
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
if (activeTickBox != null) activeTickBox.Selected = value;
|
||||
if (passiveTickBox != null) passiveTickBox.Selected = !value;
|
||||
if (activeTickBox != null) activeTickBox.Selected = value == Mode.Active;
|
||||
if (passiveTickBox != null) passiveTickBox.Selected = value == Mode.Passive;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -112,8 +130,9 @@ namespace Barotrauma.Items.Components
|
||||
: base(item, element)
|
||||
{
|
||||
connectedTransducers = new List<ConnectedTransducer>();
|
||||
|
||||
IsActive = false;
|
||||
|
||||
CurrentMode = Mode.Passive;
|
||||
IsActive = true;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
@@ -133,40 +152,80 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
connectedTransducers.RemoveAll(t => t.DisconnectTimer <= 0.0f);
|
||||
}
|
||||
|
||||
if ((voltage >= minVoltage || powerConsumption <= 0.0f) &&
|
||||
(!UseTransducers || connectedTransducers.Count > 0))
|
||||
|
||||
for (var pingIndex = 0; pingIndex < activePingsCount; ++pingIndex)
|
||||
{
|
||||
pingState = pingState + deltaTime * 0.5f;
|
||||
if (pingState > 1.0f)
|
||||
activePings[pingIndex].State += deltaTime * PingFrequency;
|
||||
}
|
||||
|
||||
if (currentMode == Mode.Active)
|
||||
{
|
||||
if ((voltage >= minVoltage || powerConsumption <= 0.0f) &&
|
||||
(!UseTransducers || connectedTransducers.Count > 0))
|
||||
{
|
||||
if (currentPingIndex != -1)
|
||||
{
|
||||
var activePing = activePings[currentPingIndex];
|
||||
if (activePing.State > 1.0f)
|
||||
{
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
item.CurrentHull.AiTarget.SoundRange = Math.Max(Range * activePing.State / zoom, item.CurrentHull.AiTarget.SoundRange);
|
||||
item.CurrentHull.AiTarget.SectorDegrees = activePing.IsDirectional ? DirectionalPingSector : 360.0f;
|
||||
item.CurrentHull.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
|
||||
}
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
item.AiTarget.SoundRange = Math.Max(Range * activePing.State / zoom, item.AiTarget.SoundRange);
|
||||
item.AiTarget.SectorDegrees = activePing.IsDirectional ? DirectionalPingSector : 360.0f;
|
||||
item.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
|
||||
}
|
||||
aiPingCheckPending = true;
|
||||
currentPingIndex = -1;
|
||||
}
|
||||
}
|
||||
if (currentPingIndex == -1 && activePingsCount < activePings.Length)
|
||||
{
|
||||
currentPingIndex = activePingsCount++;
|
||||
if (activePings[currentPingIndex] == null)
|
||||
{
|
||||
activePings[currentPingIndex] = new ActivePing();
|
||||
}
|
||||
activePings[currentPingIndex].IsDirectional = useDirectionalPing;
|
||||
activePings[currentPingIndex].Direction = pingDirection;
|
||||
activePings[currentPingIndex].State = 0.0f;
|
||||
activePings[currentPingIndex].PrevPingRadius = 0.0f;
|
||||
item.Use(deltaTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
item.CurrentHull.AiTarget.SoundRange = Math.Max(Range * pingState / zoom, item.CurrentHull.AiTarget.SoundRange);
|
||||
item.CurrentHull.AiTarget.SectorDegrees = isLastPingDirectional ? DirectionalPingSector : 360.0f;
|
||||
item.CurrentHull.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
|
||||
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
|
||||
}
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
item.AiTarget.SoundRange = Math.Max(Range * pingState / zoom, item.AiTarget.SoundRange);
|
||||
item.AiTarget.SectorDegrees = isLastPingDirectional ? DirectionalPingSector : 360.0f;
|
||||
item.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
|
||||
}
|
||||
aiPingCheckPending = true;
|
||||
isLastPingDirectional = useDirectionalPing;
|
||||
lastPingDirection = pingDirection;
|
||||
item.Use(deltaTime);
|
||||
pingState = 0.0f;
|
||||
currentPingIndex = -1;
|
||||
aiPingCheckPending = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
for (var pingIndex = 0; pingIndex < activePingsCount;)
|
||||
{
|
||||
if (item.CurrentHull != null)
|
||||
if (activePings[pingIndex].State > 1.0f)
|
||||
{
|
||||
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
|
||||
var lastIndex = --activePingsCount;
|
||||
var oldActivePing = activePings[pingIndex];
|
||||
activePings[pingIndex] = activePings[lastIndex];
|
||||
activePings[lastIndex] = oldActivePing;
|
||||
if (currentPingIndex == lastIndex)
|
||||
{
|
||||
currentPingIndex = pingIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++pingIndex;
|
||||
}
|
||||
aiPingCheckPending = false;
|
||||
pingState = 0.0f;
|
||||
}
|
||||
|
||||
Voltage -= deltaTime;
|
||||
@@ -174,7 +233,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
return pingState > 1.0f;
|
||||
return currentPingIndex != -1;
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
@@ -189,7 +248,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (!IsActive || !aiPingCheckPending) return false;
|
||||
if (currentMode == Mode.Passive || !aiPingCheckPending) return false;
|
||||
|
||||
Dictionary<string, List<Character>> targetGroups = new Dictionary<string, List<Character>>();
|
||||
|
||||
@@ -301,13 +360,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.CanClientAccess(c)) return;
|
||||
if (!item.CanClientAccess(c)) return;
|
||||
|
||||
IsActive = isActive;
|
||||
CurrentMode = isActive ? Mode.Active : Mode.Passive;
|
||||
|
||||
//TODO: cleanup
|
||||
#if CLIENT
|
||||
activeTickBox.Selected = IsActive;
|
||||
activeTickBox.Selected = currentMode == Mode.Active;
|
||||
#endif
|
||||
if (isActive)
|
||||
{
|
||||
@@ -331,8 +390,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(IsActive);
|
||||
if (IsActive)
|
||||
msg.Write(currentMode == Mode.Active);
|
||||
if (currentMode == Mode.Active)
|
||||
{
|
||||
msg.WriteRangedSingle(zoom, MinZoom, MaxZoom, 8);
|
||||
msg.Write(useDirectionalPing);
|
||||
|
||||
@@ -198,11 +198,7 @@ namespace Barotrauma.Items.Components
|
||||
if (pt.item.Condition <= 0.0f && prevCondition > 0.0f)
|
||||
{
|
||||
#if CLIENT
|
||||
if (sparkSounds.Count > 0)
|
||||
{
|
||||
var sparkSound = sparkSounds[Rand.Int(sparkSounds.Count)];
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, pt.item.WorldPosition, sparkSound.Volume, sparkSound.Range, pt.item.CurrentHull);
|
||||
}
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
|
||||
Vector2 baseVel = Rand.Vector(300.0f);
|
||||
for (int i = 0; i < 10; i++)
|
||||
@@ -333,22 +329,22 @@ namespace Barotrauma.Items.Components
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (recipient?.Item == null) continue;
|
||||
if (recipient?.Item == null || !recipient.IsPower) { continue; }
|
||||
|
||||
Item it = recipient.Item;
|
||||
if (it.Condition <= 0.0f) continue;
|
||||
if (it.Condition <= 0.0f) { continue; }
|
||||
|
||||
foreach (ItemComponent ic in it.Components)
|
||||
{
|
||||
if (!(ic is Powered powered) || !powered.IsActive) continue;
|
||||
if (connectedList.Contains(powered)) continue;
|
||||
if (!(ic is Powered powered) || !powered.IsActive) { continue; }
|
||||
if (connectedList.Contains(powered)) { continue; }
|
||||
|
||||
if (powered is PowerTransfer powerTransfer)
|
||||
{
|
||||
RelayComponent otherRelayComponent = powerTransfer as RelayComponent;
|
||||
if ((thisRelayComponent == null) == (otherRelayComponent == null))
|
||||
{
|
||||
if (!powerTransfer.CanTransfer) continue;
|
||||
if (!powerTransfer.CanTransfer) { continue; }
|
||||
powerTransfer.CheckJunctions(deltaTime, increaseUpdateCount, clampPower, clampLoad);
|
||||
}
|
||||
else
|
||||
@@ -358,7 +354,7 @@ namespace Barotrauma.Items.Components
|
||||
float maxPowerOut = (thisRelayComponent != null && !c.IsOutput) ? 0.0f : clampLoad;
|
||||
if (maxPowerIn > 0.0f || maxPowerOut > 0.0f)
|
||||
{
|
||||
powerTransfer.CheckJunctions(deltaTime, false, maxPowerIn, maxPowerOut);
|
||||
powerTransfer.CheckJunctions(deltaTime, false, maxPowerIn, maxPowerOut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +451,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
bool broken = recipient.Item.Condition <= 0.0f;
|
||||
foreach (StatusEffect effect in recipient.effects)
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
if (broken && effect.type != ActionType.OnBroken) continue;
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f, null, null, false, false);
|
||||
|
||||
@@ -45,7 +45,10 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
base.IsActive = value;
|
||||
if (!value) currPowerConsumption = 0.0f;
|
||||
if (!value)
|
||||
{
|
||||
currPowerConsumption = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1, false)]
|
||||
public int HitScanCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool RemoveOnHit
|
||||
{
|
||||
@@ -120,6 +127,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float Spread
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Projectile(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
@@ -154,17 +168,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character != null && !characterUsable) return false;
|
||||
if (character != null && !characterUsable) { return false; }
|
||||
|
||||
Vector2 launchDir = new Vector2((float)Math.Cos(item.body.Rotation), (float)Math.Sin(item.body.Rotation));
|
||||
|
||||
if (Hitscan)
|
||||
for (int i = 0; i < HitScanCount; i++)
|
||||
{
|
||||
DoHitscan(launchDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
Launch(launchDir * launchImpulse * item.body.Mass);
|
||||
float launchAngle = item.body.Rotation + MathHelper.ToRadians(Rand.Range(-Spread, Spread));
|
||||
Vector2 launchDir = new Vector2((float)Math.Cos(launchAngle), (float)Math.Sin(launchAngle));
|
||||
if (Hitscan)
|
||||
{
|
||||
Vector2 prevSimpos = item.SimPosition;
|
||||
DoHitscan(launchDir);
|
||||
if (i < HitScanCount - 1)
|
||||
{
|
||||
item.SetTransform(prevSimpos, item.body.Rotation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Launch(launchDir * launchImpulse * item.body.Mass);
|
||||
}
|
||||
}
|
||||
|
||||
User = character;
|
||||
@@ -306,6 +328,9 @@ namespace Barotrauma.Items.Components
|
||||
!fixture.CollisionCategories.HasFlag(Physics.CollisionWall) &&
|
||||
!fixture.CollisionCategories.HasFlag(Physics.CollisionLevel)) return true;
|
||||
|
||||
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
|
||||
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
|
||||
|
||||
hits.Add(new HitscanResult(fixture, rayStart, -dir, 0.0f));
|
||||
return true;
|
||||
}, ref aabb);
|
||||
|
||||
@@ -189,7 +189,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Condition += deltaTime / (fixDuration / item.MaxCondition);
|
||||
float conditionIncrease = deltaTime / (fixDuration / item.MaxCondition);
|
||||
item.Condition += conditionIncrease;
|
||||
#if SERVER
|
||||
GameMain.Server.KarmaManager.OnItemRepaired(CurrentFixer, this, conditionIncrease);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (wasBroken && item.IsFullCondition)
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public readonly bool IsOutput;
|
||||
|
||||
public readonly List<StatusEffect> effects;
|
||||
public readonly List<StatusEffect> Effects;
|
||||
|
||||
public readonly ushort[] wireId;
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
IsPower = Name == "power_in" || Name == "power" || Name == "power_out";
|
||||
|
||||
effects = new List<StatusEffect>();
|
||||
Effects = new List<StatusEffect>();
|
||||
|
||||
wireId = new ushort[MaxLinked];
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
|
||||
case "statuseffect":
|
||||
effects.Add(StatusEffect.Load(subElement, item.Name + ", connection " + Name));
|
||||
Effects.Add(StatusEffect.Load(subElement, item.Name + ", connection " + Name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -222,6 +222,7 @@ namespace Barotrauma.Items.Components
|
||||
recipientsDirty = true;
|
||||
if (wire != null)
|
||||
{
|
||||
ConnectionPanel.DisconnectedWires.Remove(wire);
|
||||
var otherConnection = wire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
{
|
||||
@@ -251,10 +252,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
bool broken = recipient.Item.Condition <= 0.0f;
|
||||
foreach (StatusEffect effect in recipient.effects)
|
||||
foreach (StatusEffect effect in recipient.Effects)
|
||||
{
|
||||
if (broken && effect.type != ActionType.OnBroken) continue;
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f, null, null, false, false);
|
||||
recipient.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step, null, null, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,11 +278,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wireId[i] == 0) continue;
|
||||
if (wireId[i] == 0) { continue; }
|
||||
|
||||
Item wireItem = Entity.FindEntityByID(wireId[i]) as Item;
|
||||
|
||||
if (wireItem == null) continue;
|
||||
if (!(Entity.FindEntityByID(wireId[i]) is Item wireItem)) { continue; }
|
||||
wires[i] = wireItem.GetComponent<Wire>();
|
||||
recipientsDirty = true;
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Character user;
|
||||
|
||||
/// <summary>
|
||||
/// Wires that have been disconnected from the panel, but not removed completely (visible at the bottom of the connection panel).
|
||||
/// </summary>
|
||||
public readonly HashSet<Wire> DisconnectedWires = new HashSet<Wire>();
|
||||
|
||||
[Serialize(false, true), Editable(ToolTip = "Locked connection panels cannot be rewired in-game.")]
|
||||
public bool Locked
|
||||
{
|
||||
@@ -103,6 +108,23 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
#if CLIENT
|
||||
foreach (Wire wire in DisconnectedWires)
|
||||
{
|
||||
if (Rand.Range(0.0f, 500.0f) < 1.0f)
|
||||
{
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
Vector2 baseVel = new Vector2(0.0f, -100.0f);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition,
|
||||
baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull);
|
||||
if (particle != null) { particle.Size *= Rand.Range(0.5f, 1.0f); }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (user == null || user.SelectedConstruction != item)
|
||||
{
|
||||
user = null;
|
||||
@@ -192,11 +214,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
DisconnectedWires.Clear();
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
foreach (Wire wire in c.Wires)
|
||||
{
|
||||
if (wire == null) continue;
|
||||
if (wire == null) { continue; }
|
||||
|
||||
if (wire.OtherConnection(c) == null) //wire not connected to anything else
|
||||
{
|
||||
@@ -219,6 +242,12 @@ namespace Barotrauma.Items.Components
|
||||
msg.Write(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((ushort)DisconnectedWires.Count());
|
||||
foreach (Wire disconnectedWire in DisconnectedWires)
|
||||
{
|
||||
msg.Write(disconnectedWire.Item.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +165,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
itemLoaded = true;
|
||||
#if CLIENT
|
||||
light.Color = IsActive ? lightColor : Color.Transparent;
|
||||
if (!IsActive) lightBrightness = 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
@@ -217,10 +221,9 @@ namespace Barotrauma.Items.Components
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.05f && voltage < Rand.Range(0.0f, minVoltage))
|
||||
{
|
||||
#if CLIENT
|
||||
if (voltage > 0.1f && sparkSounds.Count > 0)
|
||||
if (voltage > 0.1f)
|
||||
{
|
||||
var sparkSound = sparkSounds[Rand.Int(sparkSounds.Count)];
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, item.WorldPosition, sparkSound.Volume, sparkSound.Range, item.CurrentHull);
|
||||
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
|
||||
}
|
||||
#endif
|
||||
lightBrightness = 0.0f;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Barotrauma.Networking;
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -11,6 +12,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool isOn;
|
||||
|
||||
private static readonly Dictionary<string, string> connectionPairs = new Dictionary<string, string>
|
||||
{
|
||||
{ "power_in", "power_out"},
|
||||
{ "signal_in", "signal_out" },
|
||||
{ "signal_in1", "signal_out1" },
|
||||
{ "signal_in2", "signal_out2" },
|
||||
{ "signal_in3", "signal_out3" },
|
||||
{ "signal_in4", "signal_out4" },
|
||||
{ "signal_in5", "signal_out5" }
|
||||
};
|
||||
|
||||
[Editable, Serialize(1000.0f, true)]
|
||||
public float MaxPower
|
||||
{
|
||||
@@ -59,17 +71,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection.IsPower || item.Condition <= 0.0f) return;
|
||||
if (connection.IsPower || item.Condition <= 0.0f) { return; }
|
||||
|
||||
if (connection.Name.Contains("_in"))
|
||||
if (connectionPairs.TryGetValue(connection.Name, out string outConnection))
|
||||
{
|
||||
if (!IsOn) return;
|
||||
|
||||
string outConnection = connection.Name.Contains("power_in") ? "power_out" : "signal_out";
|
||||
|
||||
int connectionNumber = -1;
|
||||
int.TryParse(connection.Name.Substring(connection.Name.Length - 1, 1), out connectionNumber);
|
||||
if (connectionNumber > 0) outConnection += connectionNumber;
|
||||
if (!IsOn) { return; }
|
||||
item.SendSignal(stepsTaken, signal, outConnection, sender, power, source, signalStrength);
|
||||
}
|
||||
else if (connection.Name == "toggle")
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool CanTransmit()
|
||||
{
|
||||
return HasRequiredContainedItems(true);
|
||||
return HasRequiredContainedItems(user: null, addMessage: false);
|
||||
}
|
||||
|
||||
public IEnumerable<WifiComponent> GetReceiversInRange()
|
||||
@@ -89,7 +89,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (Vector2.DistanceSquared(item.WorldPosition, sender.item.WorldPosition) > sender.range * sender.range) { return false; }
|
||||
|
||||
return HasRequiredContainedItems(false);
|
||||
return HasRequiredContainedItems(user: null, addMessage: false);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Barotrauma.Items.Components
|
||||
private Vector2 start;
|
||||
private Vector2 end;
|
||||
|
||||
private float angle;
|
||||
private float length;
|
||||
private readonly float angle;
|
||||
private readonly float length;
|
||||
|
||||
public Vector2 Start
|
||||
{
|
||||
@@ -45,7 +45,7 @@ namespace Barotrauma.Items.Components
|
||||
const int MaxNodesPerNetworkEvent = 30;
|
||||
|
||||
private List<Vector2> nodes;
|
||||
private List<WireSection> sections;
|
||||
private readonly List<WireSection> sections;
|
||||
|
||||
private Connection[] connections;
|
||||
|
||||
@@ -85,24 +85,23 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
if (wireSprite == null)
|
||||
{
|
||||
wireSprite = new Sprite("Content/Items/wireHorizontal.png", new Vector2(0.5f, 0.5f));
|
||||
wireSprite.Depth = 0.85f;
|
||||
wireSprite = new Sprite("Content/Items/wireHorizontal.png", new Vector2(0.5f, 0.5f))
|
||||
{
|
||||
Depth = 0.85f
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
nodes = new List<Vector2>();
|
||||
sections = new List<WireSection>();
|
||||
|
||||
connections = new Connection[2];
|
||||
|
||||
connections = new Connection[2];
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
|
||||
public Connection OtherConnection(Connection connection)
|
||||
{
|
||||
if (connection == null) return null;
|
||||
if (connection == connections[0]) return connections[1];
|
||||
if (connection == connections[1]) return connections[0];
|
||||
if (connection == connections[0]) { return connections[1]; }
|
||||
if (connection == connections[1]) { return connections[0]; }
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -133,8 +132,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void RemoveConnection(Connection connection)
|
||||
{
|
||||
if (connection == connections[0]) connections[0] = null;
|
||||
if (connection == connections[1]) connections[1] = null;
|
||||
if (connection == connections[0]) { connections[0] = null; }
|
||||
if (connection == connections[1]) { connections[1] = null; }
|
||||
|
||||
SetConnectedDirty();
|
||||
}
|
||||
@@ -143,10 +142,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] == newConnection) return false;
|
||||
if (connections[i] == newConnection) { return false; }
|
||||
}
|
||||
|
||||
if (!connections.Any(c => c == null)) return false;
|
||||
if (!connections.Any(c => c == null)) { return false; }
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@@ -156,37 +155,39 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (item.body != null) { item.Submarine = newConnection.Item.Submarine; }
|
||||
|
||||
if (item.body != null) item.Submarine = newConnection.Item.Submarine;
|
||||
newConnection.ConnectionPanel.DisconnectedWires.Remove(this);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] != null) continue;
|
||||
if (connections[i] != null) { continue; }
|
||||
|
||||
connections[i] = newConnection;
|
||||
FixNodeEnds();
|
||||
|
||||
if (!addNode) break;
|
||||
if (!addNode) { break; }
|
||||
|
||||
Submarine refSub = newConnection.Item.Submarine;
|
||||
if (refSub == null)
|
||||
{
|
||||
Structure attachTarget = Structure.GetAttachTarget(newConnection.Item.WorldPosition);
|
||||
if (attachTarget == null) continue;
|
||||
if (attachTarget == null) { continue; }
|
||||
refSub = attachTarget.Submarine;
|
||||
}
|
||||
|
||||
Vector2 nodePos = refSub == null ?
|
||||
newConnection.Item.Position :
|
||||
newConnection.Item.Position - refSub.HiddenSubPosition;
|
||||
|
||||
if (nodes.Count > 0 && nodes[0] == nodePos) break;
|
||||
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) break;
|
||||
|
||||
if (nodes.Count > 0 && nodes[0] == nodePos) { break; }
|
||||
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { break; }
|
||||
|
||||
//make sure we place the node at the correct end of the wire (the end that's closest to the new node pos)
|
||||
int newNodeIndex = 0;
|
||||
if (nodes.Count > 1)
|
||||
{
|
||||
if (Vector2.DistanceSquared(nodes[nodes.Count-1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos))
|
||||
if (Vector2.DistanceSquared(nodes[nodes.Count - 1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos))
|
||||
{
|
||||
newNodeIndex = nodes.Count;
|
||||
}
|
||||
@@ -244,21 +245,18 @@ namespace Barotrauma.Items.Components
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
ClearConnections(character);
|
||||
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
ClearConnections(character);
|
||||
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
{
|
||||
ClearConnections(dropper);
|
||||
|
||||
ClearConnections(dropper);
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
@@ -399,7 +397,6 @@ namespace Barotrauma.Items.Components
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
ClearConnections(picker);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -467,9 +464,26 @@ namespace Barotrauma.Items.Components
|
||||
nodes.Clear();
|
||||
sections.Clear();
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null && connectionPanel.DisconnectedWires.Contains(this))
|
||||
{
|
||||
#if SERVER
|
||||
item.CreateServerEvent(connectionPanel);
|
||||
#endif
|
||||
connectionPanel.DisconnectedWires.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (user != null)
|
||||
{
|
||||
if (connections[0] != null || connections[1] != null)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnWireDisconnected(user, this);
|
||||
}
|
||||
|
||||
if (connections[0] != null && connections[1] != null)
|
||||
{
|
||||
GameServer.Log(user.LogName + " disconnected a wire from " +
|
||||
@@ -488,17 +502,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
SetConnectedDirty();
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] == null) continue;
|
||||
if (connections[i] == null) { continue; }
|
||||
int wireIndex = connections[i].FindWireIndex(item);
|
||||
|
||||
if (wireIndex == -1) continue;
|
||||
if (wireIndex == -1) { continue; }
|
||||
#if SERVER
|
||||
if (!connections[i].Item.Removed)
|
||||
{
|
||||
connections[i].Item.CreateServerEvent(connections[i].Item.GetComponent<ConnectionPanel>());
|
||||
}
|
||||
#endif
|
||||
connections[i].SetWire(wireIndex, null);
|
||||
|
||||
connections[i] = null;
|
||||
}
|
||||
|
||||
@@ -565,7 +583,27 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
} while (removed);
|
||||
}
|
||||
|
||||
private void FixNodeEnds()
|
||||
{
|
||||
if (connections[0] == null || connections[1] == null || nodes.Count == 0) { return; }
|
||||
|
||||
Vector2 nodePos = nodes[0];
|
||||
|
||||
Submarine refSub = connections[0].Item.Submarine ?? connections[1].Item.Submarine;
|
||||
if (refSub != null) { nodePos += refSub.HiddenSubPosition; }
|
||||
|
||||
float dist1 = Vector2.DistanceSquared(connections[0].Item.Position, nodePos);
|
||||
float dist2 = Vector2.DistanceSquared(connections[1].Item.Position, nodePos);
|
||||
|
||||
//first node is closer to the second item
|
||||
//= the nodes are "backwards", need to reverse them
|
||||
if (dist1 > dist2)
|
||||
{
|
||||
nodes.Reverse();
|
||||
UpdateSections();
|
||||
}
|
||||
}
|
||||
|
||||
private int GetClosestNodeIndex(Vector2 pos, float maxDist, out float closestDist)
|
||||
@@ -640,12 +678,8 @@ namespace Barotrauma.Items.Components
|
||||
string[] nodeCoords = nodeString.Split(';');
|
||||
for (int i = 0; i < nodeCoords.Length / 2; i++)
|
||||
{
|
||||
float x = 0.0f, y = 0.0f;
|
||||
|
||||
float.TryParse(nodeCoords[i * 2], NumberStyles.Float, CultureInfo.InvariantCulture, out x);
|
||||
|
||||
float.TryParse(nodeCoords[i * 2 + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out y);
|
||||
|
||||
float.TryParse(nodeCoords[i * 2], NumberStyles.Float, CultureInfo.InvariantCulture, out float x);
|
||||
float.TryParse(nodeCoords[i * 2 + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out float y);
|
||||
nodes.Add(new Vector2(x, y));
|
||||
}
|
||||
|
||||
@@ -687,7 +721,6 @@ namespace Barotrauma.Items.Components
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
ClearConnections();
|
||||
|
||||
base.RemoveComponentSpecific();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Barotrauma
|
||||
Moustache,
|
||||
FaceAttachment,
|
||||
JobIndicator,
|
||||
Husk
|
||||
Husk,
|
||||
Herpes
|
||||
}
|
||||
|
||||
class WearableSprite
|
||||
@@ -101,6 +102,7 @@ namespace Barotrauma
|
||||
case WearableType.FaceAttachment:
|
||||
case WearableType.JobIndicator:
|
||||
case WearableType.Husk:
|
||||
case WearableType.Herpes:
|
||||
Limb = LimbType.Head;
|
||||
HideLimb = false;
|
||||
HideOtherWearables = false;
|
||||
@@ -207,6 +209,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get { return damageModifiers; }
|
||||
}
|
||||
|
||||
private bool autoEquipWhenFull;
|
||||
public bool AutoEquipWhenFull
|
||||
{
|
||||
get { return autoEquipWhenFull; }
|
||||
}
|
||||
|
||||
public Wearable(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
@@ -220,6 +228,7 @@ namespace Barotrauma.Items.Components
|
||||
wearableSprites = new WearableSprite[spriteCount];
|
||||
limbType = new LimbType[spriteCount];
|
||||
limb = new Limb[spriteCount];
|
||||
autoEquipWhenFull = element.GetAttributeBool("autoequipwhenfull", true);
|
||||
int i = 0;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
if (scale == value) { return; }
|
||||
scale = MathHelper.Clamp(value, 0.1f, 10.0f);
|
||||
scale = MathHelper.Clamp(value, 0.01f, 10.0f);
|
||||
|
||||
float relativeScale = scale / prefab.Scale;
|
||||
|
||||
@@ -288,7 +288,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Can be used by status effects or conditionals to modify the sound range
|
||||
/// </summary>
|
||||
public float SoundRange
|
||||
public new float SoundRange
|
||||
{
|
||||
get { return aiTarget == null ? 0.0f : aiTarget.SoundRange; }
|
||||
set { if (aiTarget != null) { aiTarget.SoundRange = Math.Max(0.0f, value); } }
|
||||
@@ -298,7 +298,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Can be used by status effects or conditionals to modify the sound range
|
||||
/// </summary>
|
||||
public float SightRange
|
||||
public new float SightRange
|
||||
{
|
||||
get { return aiTarget == null ? 0.0f : aiTarget.SightRange; }
|
||||
set { if (aiTarget != null) { aiTarget.SightRange = Math.Max(0.0f, value); } }
|
||||
@@ -1154,7 +1154,14 @@ namespace Barotrauma
|
||||
{
|
||||
ic.Update(deltaTime, cam);
|
||||
#if CLIENT
|
||||
if (ic.IsActive) ic.PlaySound(ActionType.OnActive, WorldPosition);
|
||||
if (ic.IsActive)
|
||||
{
|
||||
if (ic.IsActiveTimer > 0.02f)
|
||||
{
|
||||
ic.PlaySound(ActionType.OnActive, WorldPosition);
|
||||
}
|
||||
ic.IsActiveTimer += deltaTime;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1381,7 +1388,7 @@ namespace Barotrauma
|
||||
return connectedComponents;
|
||||
}
|
||||
|
||||
private static readonly Pair<string, string>[] connectionPairs = new Pair<string, string>[]
|
||||
public static readonly Pair<string, string>[] connectionPairs = new Pair<string, string>[]
|
||||
{
|
||||
new Pair<string, string>("power_in", "power_out"),
|
||||
new Pair<string, string>("signal_in1", "signal_out1"),
|
||||
@@ -1453,11 +1460,11 @@ namespace Barotrauma
|
||||
public void SendSignal(int stepsTaken, string signal, string connectionName, Character sender, float power = 0.0f, Item source = null, float signalStrength = 1.0f)
|
||||
{
|
||||
LastSentSignalRecipients.Clear();
|
||||
if (connections == null) return;
|
||||
if (connections == null) { return; }
|
||||
|
||||
stepsTaken++;
|
||||
|
||||
if (!connections.TryGetValue(connectionName, out Connection c)) return;
|
||||
if (!connections.TryGetValue(connectionName, out Connection c)) { return; }
|
||||
|
||||
if (stepsTaken > 10)
|
||||
{
|
||||
@@ -1467,6 +1474,11 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (StatusEffect effect in c.Effects)
|
||||
{
|
||||
if (condition <= 0.0f && effect.type != ActionType.OnBroken) { continue; }
|
||||
if (signal != "0" && !string.IsNullOrEmpty(signal)) { ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step, null, null, false, false); }
|
||||
}
|
||||
c.SendSignal(stepsTaken, signal, source ?? this, sender, power, signalStrength);
|
||||
}
|
||||
}
|
||||
@@ -1519,47 +1531,50 @@ namespace Barotrauma
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
bool pickHit = false, selectHit = false;
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
|
||||
if (picker.IsKeyDown(InputType.Aim))
|
||||
{
|
||||
pickHit = picker.IsKeyHit(InputType.Select);
|
||||
selectHit = picker.IsKeyHit(InputType.Select);
|
||||
pickHit = false;
|
||||
selectHit = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (picker.IsKeyDown(InputType.Aim))
|
||||
if (forceSelectKey)
|
||||
{
|
||||
pickHit = false;
|
||||
selectHit = false;
|
||||
if (ic.PickKey == InputType.Select) pickHit = true;
|
||||
if (ic.SelectKey == InputType.Select) selectHit = true;
|
||||
}
|
||||
else if (forceActionKey)
|
||||
{
|
||||
if (ic.PickKey == InputType.Use) pickHit = true;
|
||||
if (ic.SelectKey == InputType.Use) selectHit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (forceSelectKey)
|
||||
{
|
||||
if (ic.PickKey == InputType.Select) pickHit = true;
|
||||
if (ic.SelectKey == InputType.Select) selectHit = true;
|
||||
}
|
||||
else if (forceActionKey)
|
||||
{
|
||||
if (ic.PickKey == InputType.Use) pickHit = true;
|
||||
if (ic.SelectKey == InputType.Use) selectHit = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pickHit = picker.IsKeyHit(ic.PickKey);
|
||||
selectHit = picker.IsKeyHit(ic.SelectKey);
|
||||
pickHit = picker.IsKeyHit(ic.PickKey);
|
||||
selectHit = picker.IsKeyHit(ic.SelectKey);
|
||||
|
||||
#if CLIENT
|
||||
//if the cursor is on a UI component, disable interaction with the left mouse button
|
||||
//to prevent accidentally selecting items when clicking UI elements
|
||||
if (picker == Character.Controlled && GUI.MouseOn != null)
|
||||
{
|
||||
if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0) pickHit = false;
|
||||
if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0) selectHit = false;
|
||||
}
|
||||
#endif
|
||||
//if the cursor is on a UI component, disable interaction with the left mouse button
|
||||
//to prevent accidentally selecting items when clicking UI elements
|
||||
if (picker == Character.Controlled && GUI.MouseOn != null)
|
||||
{
|
||||
if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0) pickHit = false;
|
||||
if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0) selectHit = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
//use the non-mouse interaction key (E on both default and legacy keybinds) in wiring mode
|
||||
//LMB is used to manipulate wires, so using E to select connection panels is much easier
|
||||
if (Screen.Selected == GameMain.SubEditorScreen && GameMain.SubEditorScreen.WiringMode)
|
||||
{
|
||||
pickHit = selectHit = GameMain.Config.KeyBind(InputType.Use).MouseButton == null ?
|
||||
picker.IsKeyHit(InputType.Use) :
|
||||
picker.IsKeyHit(InputType.Select);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!pickHit && !selectHit) continue;
|
||||
|
||||
@@ -1621,8 +1636,8 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (condition == 0.0f) return;
|
||||
|
||||
if (condition == 0.0f) { return; }
|
||||
|
||||
bool remove = false;
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
@@ -1631,7 +1646,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
isControlled = character == Character.Controlled;
|
||||
#endif
|
||||
if (!ic.HasRequiredContainedItems(isControlled)) continue;
|
||||
if (!ic.HasRequiredContainedItems(character, isControlled)) { continue; }
|
||||
if (ic.Use(deltaTime, character))
|
||||
{
|
||||
ic.WasUsed = true;
|
||||
@@ -1642,7 +1657,7 @@ namespace Barotrauma
|
||||
|
||||
ic.ApplyStatusEffects(ActionType.OnUse, deltaTime, character, targetLimb);
|
||||
|
||||
if (ic.DeleteOnUse) remove = true;
|
||||
if (ic.DeleteOnUse) { remove = true; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1654,7 +1669,7 @@ namespace Barotrauma
|
||||
|
||||
public void SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (condition == 0.0f) return;
|
||||
if (condition == 0.0f) { return; }
|
||||
|
||||
bool remove = false;
|
||||
|
||||
@@ -1664,7 +1679,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
isControlled = character == Character.Controlled;
|
||||
#endif
|
||||
if (!ic.HasRequiredContainedItems(isControlled)) continue;
|
||||
if (!ic.HasRequiredContainedItems(character, isControlled)) { continue; }
|
||||
if (ic.SecondaryUse(deltaTime, character))
|
||||
{
|
||||
ic.WasUsed = true;
|
||||
@@ -1675,7 +1690,7 @@ namespace Barotrauma
|
||||
|
||||
ic.ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, character);
|
||||
|
||||
if (ic.DeleteOnUse) remove = true;
|
||||
if (ic.DeleteOnUse) { remove = true; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1702,7 +1717,7 @@ namespace Barotrauma
|
||||
bool remove = false;
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
if (!ic.HasRequiredContainedItems(user == Character.Controlled)) continue;
|
||||
if (!ic.HasRequiredContainedItems(user, addMessage: user == Character.Controlled)) continue;
|
||||
|
||||
bool success = Rand.Range(0.0f, 0.5f) < ic.DegreeOfSuccess(user);
|
||||
ActionType actionType = success ? ActionType.OnUse : ActionType.OnFailure;
|
||||
@@ -1713,7 +1728,7 @@ namespace Barotrauma
|
||||
ic.WasUsed = true;
|
||||
ic.ApplyStatusEffects(actionType, 1.0f, character, targetLimb, user: user);
|
||||
|
||||
if (GameMain.NetworkMember!=null && GameMain.NetworkMember.IsServer)
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[]
|
||||
{
|
||||
@@ -1729,11 +1744,15 @@ namespace Barotrauma
|
||||
|
||||
public bool Combine(Item item)
|
||||
{
|
||||
if (item == this) { return false; }
|
||||
bool isCombined = false;
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
if (ic.Combine(item)) isCombined = true;
|
||||
if (ic.Combine(item)) { isCombined = true; }
|
||||
}
|
||||
#if CLIENT
|
||||
if (isCombined) { GameMain.Client?.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Combine, item.ID }); }
|
||||
#endif
|
||||
return isCombined;
|
||||
}
|
||||
|
||||
|
||||
@@ -412,8 +412,12 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
XDocument doc = XMLExtensions.TryLoadXml(filePath);
|
||||
if (doc?.Root == null) { return; }
|
||||
|
||||
if (doc?.Root == null)
|
||||
{
|
||||
DebugConsole.ThrowError("File \"" + filePath + "\" could not be loaded.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (doc.Root.Name.ToString().ToLowerInvariant() == "items")
|
||||
{
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
|
||||
@@ -95,41 +95,25 @@ namespace Barotrauma
|
||||
switch (type)
|
||||
{
|
||||
case RelationType.Contained:
|
||||
if (parentItem == null) return false;
|
||||
|
||||
var containedItems = parentItem.ContainedItems;
|
||||
if (containedItems == null) return false;
|
||||
|
||||
if (MatchOnEmpty && !containedItems.Any(ci => ci != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (Item contained in containedItems)
|
||||
{
|
||||
if (contained.Condition > 0.0f && MatchesItem(contained)) return true;
|
||||
}
|
||||
break;
|
||||
if (parentItem == null) { return false; }
|
||||
return CheckContained(parentItem);
|
||||
case RelationType.Container:
|
||||
if (parentItem == null || parentItem.Container == null) return false;
|
||||
|
||||
if (parentItem == null || parentItem.Container == null) { return false; }
|
||||
return parentItem.Container.Condition > 0.0f && MatchesItem(parentItem.Container);
|
||||
case RelationType.Equipped:
|
||||
if (character == null) return false;
|
||||
if (character == null) { return false; }
|
||||
foreach (Item equippedItem in character.SelectedItems)
|
||||
{
|
||||
if (equippedItem == null) continue;
|
||||
|
||||
if (equippedItem.Condition > 0.0f && MatchesItem(equippedItem)) return true;
|
||||
if (equippedItem == null) { continue; }
|
||||
if (equippedItem.Condition > 0.0f && MatchesItem(equippedItem)) { return true; }
|
||||
}
|
||||
break;
|
||||
case RelationType.Picked:
|
||||
if (character == null || character.Inventory == null) return false;
|
||||
if (character == null || character.Inventory == null) { return false; }
|
||||
foreach (Item pickedItem in character.Inventory.Items)
|
||||
{
|
||||
if (pickedItem == null) continue;
|
||||
|
||||
if (MatchesItem(pickedItem)) return true;
|
||||
if (pickedItem == null) { continue; }
|
||||
if (MatchesItem(pickedItem)) { return true; }
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -139,6 +123,25 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckContained(Item parentItem)
|
||||
{
|
||||
var containedItems = parentItem.ContainedItems;
|
||||
if (containedItems == null) { return false; }
|
||||
|
||||
if (MatchOnEmpty && !containedItems.Any(ci => ci != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (Item contained in containedItems)
|
||||
{
|
||||
if (contained == null) { continue; }
|
||||
if (contained.Condition > 0.0f && MatchesItem(contained)) { return true; }
|
||||
if (CheckContained(contained)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Save(XElement element)
|
||||
{
|
||||
element.Add(
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma
|
||||
return prevExplosions.FindAll(e => e.Third >= Timing.TotalTime - maxSecondsAgo);
|
||||
}
|
||||
|
||||
public void Explode(Vector2 worldPosition, Entity damageSource)
|
||||
public void Explode(Vector2 worldPosition, Entity damageSource, Character attacker = null)
|
||||
{
|
||||
prevExplosions.Add(new Triplet<Explosion, Vector2, float>(this, worldPosition, (float)Timing.TotalTime));
|
||||
if (prevExplosions.Count > 100)
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma
|
||||
|
||||
if (attack.GetStructureDamage(1.0f) > 0.0f)
|
||||
{
|
||||
RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f));
|
||||
RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f), attacker);
|
||||
}
|
||||
|
||||
if (empStrength > 0.0f)
|
||||
@@ -130,7 +130,7 @@ namespace Barotrauma
|
||||
|
||||
if (force == 0.0f && attack.Stun == 0.0f && attack.GetTotalDamage(false) == 0.0f) return;
|
||||
|
||||
DamageCharacters(worldPosition, attack, force, damageSource);
|
||||
DamageCharacters(worldPosition, attack, force, damageSource, attacker);
|
||||
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
@@ -163,17 +163,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull);
|
||||
|
||||
|
||||
private Vector2 ClampParticlePos(Vector2 particlePos, Hull hull)
|
||||
{
|
||||
if (hull == null) return particlePos;
|
||||
|
||||
return new Vector2(
|
||||
MathHelper.Clamp(particlePos.X, hull.WorldRect.X, hull.WorldRect.Right),
|
||||
MathHelper.Clamp(particlePos.Y, hull.WorldRect.Y - hull.WorldRect.Height, hull.WorldRect.Y));
|
||||
}
|
||||
|
||||
public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource)
|
||||
public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
|
||||
{
|
||||
if (attack.Range <= 0.0f) return;
|
||||
|
||||
@@ -222,11 +214,13 @@ namespace Barotrauma
|
||||
modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length));
|
||||
}
|
||||
c.LastDamageSource = damageSource;
|
||||
Character attacker = null;
|
||||
if (damageSource is Item item)
|
||||
if (attacker == null)
|
||||
{
|
||||
attacker = item.GetComponent<Projectile>()?.User;
|
||||
if (attacker == null) attacker = item.GetComponent<MeleeWeapon>()?.User;
|
||||
if (damageSource is Item item)
|
||||
{
|
||||
attacker = item.GetComponent<Projectile>()?.User;
|
||||
if (attacker == null) attacker = item.GetComponent<MeleeWeapon>()?.User;
|
||||
}
|
||||
}
|
||||
|
||||
//use a position slightly from the limb's position towards the explosion
|
||||
@@ -280,7 +274,7 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken
|
||||
/// </summary>
|
||||
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage)
|
||||
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, Character attacker = null)
|
||||
{
|
||||
List<Structure> structureList = new List<Structure>();
|
||||
float dist = 600.0f;
|
||||
@@ -304,7 +298,7 @@ namespace Barotrauma
|
||||
float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange);
|
||||
if (distFactor <= 0.0f) continue;
|
||||
|
||||
structure.AddDamage(i, damage * distFactor);
|
||||
structure.AddDamage(i, damage * distFactor, attacker);
|
||||
|
||||
if (damagedStructures.ContainsKey(structure))
|
||||
{
|
||||
|
||||
@@ -853,7 +853,7 @@ namespace Barotrauma
|
||||
return tooCloseCells;
|
||||
}
|
||||
|
||||
private List<VoronoiCell> GetTooCloseCells(Vector2 position, float minDistance)
|
||||
public List<VoronoiCell> GetTooCloseCells(Vector2 position, float minDistance)
|
||||
{
|
||||
List<VoronoiCell> tooCloseCells = new List<VoronoiCell>();
|
||||
|
||||
|
||||
@@ -57,7 +57,12 @@ namespace Barotrauma
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"LocationType (" + Identifier + ")";
|
||||
}
|
||||
|
||||
private LocationType(XElement element)
|
||||
{
|
||||
Identifier = element.GetAttributeString("identifier", element.Name.ToString());
|
||||
|
||||
@@ -12,9 +12,13 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
public Camera AssignedCamera;
|
||||
|
||||
private float duration;
|
||||
|
||||
private CoroutineHandle updateCoroutine;
|
||||
|
||||
public RoundEndCinematic(Submarine submarine, Camera cam, float duration)
|
||||
public RoundEndCinematic(Submarine submarine, Camera cam, float duration = 10.0f)
|
||||
: this(new List<Submarine>() { submarine }, cam, duration)
|
||||
{
|
||||
|
||||
@@ -25,9 +29,19 @@ namespace Barotrauma
|
||||
if (!submarines.Any(s => s != null)) return;
|
||||
|
||||
this.duration = duration;
|
||||
AssignedCamera = cam;
|
||||
|
||||
Running = true;
|
||||
CoroutineManager.StartCoroutine(Update(submarines, cam));
|
||||
updateCoroutine = CoroutineManager.StartCoroutine(Update(submarines, cam));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
CoroutineManager.StopCoroutines(updateCoroutine);
|
||||
Running = false;
|
||||
#if CLIENT
|
||||
GUI.ScreenOverlayColor = Color.TransparentBlack;
|
||||
#endif
|
||||
}
|
||||
|
||||
private IEnumerable<object> Update(List<Submarine> subs, Camera cam)
|
||||
@@ -72,6 +86,11 @@ namespace Barotrauma
|
||||
(minPos.Y + maxPos.Y) / 2.0f);
|
||||
cam.Translate(cameraPos - cam.Position);
|
||||
|
||||
foreach (Submarine sub in subs)
|
||||
{
|
||||
sub.PhysicsBody?.ResetDynamics();
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
cam.Zoom = MathHelper.SmoothStep(initialZoom, 0.5f, timer / duration);
|
||||
if (timer / duration > 0.9f)
|
||||
|
||||
@@ -816,8 +816,7 @@ namespace Barotrauma
|
||||
|
||||
|
||||
}
|
||||
|
||||
partial void AdjustKarma(IDamageable attacker, float amount);
|
||||
|
||||
|
||||
public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false)
|
||||
{
|
||||
@@ -972,23 +971,18 @@ namespace Barotrauma
|
||||
bool hadHole = SectionBodyDisabled(sectionIndex);
|
||||
Sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, Prefab.Health);
|
||||
|
||||
//otherwise it's possible to infinitely gain karma by welding fixed things
|
||||
if (attacker != null && damageDiff != 0.0f)
|
||||
{
|
||||
AdjustKarma(attacker, damageDiff);
|
||||
#if CLIENT
|
||||
if (GameMain.Client == null)
|
||||
OnHealthChangedProjSpecific(attacker, damageDiff);
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
#endif
|
||||
if (damageDiff < 0.0f)
|
||||
{
|
||||
attacker.Info.IncreaseSkillLevel("mechanical",
|
||||
-damageDiff * SkillIncreaseMultiplier / Math.Max(attacker.GetSkillLevel("mechanical"), 1.0f),
|
||||
SectionPosition(sectionIndex, true));
|
||||
}
|
||||
#if CLIENT
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool hasHole = SectionBodyDisabled(sectionIndex);
|
||||
@@ -998,6 +992,8 @@ namespace Barotrauma
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
partial void OnHealthChangedProjSpecific(Character attacker, float damageAmount);
|
||||
|
||||
public void SetCollisionCategory(Category collisionCategory)
|
||||
{
|
||||
if (Bodies == null) return;
|
||||
|
||||
@@ -745,6 +745,12 @@ namespace Barotrauma
|
||||
private static readonly Dictionary<Body, float> bodyDist = new Dictionary<Body, float>();
|
||||
private static readonly List<Body> bodies = new List<Body>();
|
||||
|
||||
public static float LastPickedBodyDist(Body body)
|
||||
{
|
||||
if (!bodyDist.ContainsKey(body)) { return 0.0f; }
|
||||
return bodyDist[body];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of physics bodies the ray intersects with, sorted according to distance (the closest body is at the beginning of the list).
|
||||
/// </summary>
|
||||
@@ -1067,16 +1073,14 @@ namespace Barotrauma
|
||||
//Level.Loaded.Move(-amount);
|
||||
}
|
||||
|
||||
public static Submarine FindClosest(Vector2 worldPosition, bool ignoreOutposts = false)
|
||||
public static Submarine FindClosest(Vector2 worldPosition, bool ignoreOutposts = false, bool ignoreOutsideLevel = true)
|
||||
{
|
||||
Submarine closest = null;
|
||||
float closestDist = 0.0f;
|
||||
foreach (Submarine sub in loaded)
|
||||
{
|
||||
if (ignoreOutposts && sub.IsOutpost)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (ignoreOutposts && sub.IsOutpost) { continue; }
|
||||
if (ignoreOutsideLevel && Level.Loaded != null && sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
float dist = Vector2.DistanceSquared(worldPosition, sub.WorldPosition);
|
||||
if (closest == null || dist < closestDist)
|
||||
{
|
||||
@@ -1209,7 +1213,34 @@ namespace Barotrauma
|
||||
foreach (string path in filePaths)
|
||||
{
|
||||
var sub = new Submarine(path);
|
||||
if (!sub.IsFileCorrupted)
|
||||
if (sub.IsFileCorrupted)
|
||||
{
|
||||
#if CLIENT
|
||||
if (DebugConsole.IsOpen) { DebugConsole.Toggle(); }
|
||||
var deleteSubPrompt = new GUIMessageBox(
|
||||
TextManager.Get("Error"),
|
||||
TextManager.GetWithVariable("SubLoadError", "[subname]", sub.name) +"\n"+
|
||||
TextManager.GetWithVariable("DeleteFileVerification", "[filename]", sub.name),
|
||||
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
|
||||
|
||||
string filePath = path;
|
||||
deleteSubPrompt.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to delete file \"{filePath}\".", e);
|
||||
}
|
||||
deleteSubPrompt.Close();
|
||||
return true;
|
||||
};
|
||||
deleteSubPrompt.Buttons[1].OnClicked += deleteSubPrompt.Close;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
savedSubmarines.Add(sub);
|
||||
}
|
||||
@@ -1621,7 +1652,6 @@ namespace Barotrauma
|
||||
if (wp.isObstructed) { continue; }
|
||||
foreach (var connection in node.connections)
|
||||
{
|
||||
bool isObstructed = false;
|
||||
var connectedWp = connection.Waypoint;
|
||||
if (connectedWp.isObstructed) { continue; }
|
||||
Vector2 start = ConvertUnits.ToSimUnits(wp.WorldPosition);
|
||||
@@ -1652,7 +1682,6 @@ namespace Barotrauma
|
||||
if (wp.isObstructed) { continue; }
|
||||
foreach (var connection in node.connections)
|
||||
{
|
||||
bool isObstructed = false;
|
||||
var connectedWp = connection.Waypoint;
|
||||
if (connectedWp.isObstructed) { continue; }
|
||||
Vector2 start = ConvertUnits.ToSimUnits(wp.WorldPosition) - otherSub.SimPosition;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -42,6 +43,27 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 spectate_position;
|
||||
public Vector2? SpectatePos
|
||||
{
|
||||
get
|
||||
{
|
||||
if (character == null || character.IsDead)
|
||||
{
|
||||
return spectate_position;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
spectate_position = value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool muted;
|
||||
public bool Muted
|
||||
{
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace Barotrauma.Networking
|
||||
ServerLog = 0x100,
|
||||
ManageSettings = 0x200,
|
||||
ManagePermissions = 0x400,
|
||||
All = 0x7ff
|
||||
KarmaImmunity = 0x800,
|
||||
All = 0xFFF
|
||||
}
|
||||
|
||||
class PermissionPreset
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class KarmaManager : ISerializableEntity
|
||||
{
|
||||
public static readonly string ConfigFile = "Data" + Path.DirectorySeparatorChar + "karmasettings.xml";
|
||||
|
||||
public string Name => "KarmaManager";
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
|
||||
|
||||
[Serialize(0.1f, true)]
|
||||
public float KarmaDecay { get; set; }
|
||||
|
||||
[Serialize(50.0f, true)]
|
||||
public float KarmaDecayThreshold { get; set; }
|
||||
|
||||
[Serialize(0.15f, true)]
|
||||
public float KarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(50.0f, true)]
|
||||
public float KarmaIncreaseThreshold { get; set; }
|
||||
|
||||
[Serialize(0.05f, true)]
|
||||
public float StructureRepairKarmaIncrease { get; set; }
|
||||
[Serialize(0.1f, true)]
|
||||
public float StructureDamageKarmaDecrease { get; set; }
|
||||
[Serialize(30.0f, true)]
|
||||
public float MaxStructureDamageKarmaDecreasePerSecond { get; set; }
|
||||
|
||||
[Serialize(0.03f, true)]
|
||||
public float ItemRepairKarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(0.5f, true)]
|
||||
public float ReactorOverheatKarmaDecrease { get; set; }
|
||||
[Serialize(30.0f, true)]
|
||||
public float ReactorMeltdownKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(0.1f, true)]
|
||||
public float DamageEnemyKarmaIncrease { get; set; }
|
||||
[Serialize(0.2f, true)]
|
||||
public float HealFriendlyKarmaIncrease { get; set; }
|
||||
[Serialize(0.25f, true)]
|
||||
public float DamageFriendlyKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
public float ExtinguishFireKarmaIncrease { get; set; }
|
||||
|
||||
|
||||
private float allowedWireDisconnectionsPerMinute;
|
||||
[Serialize(5.0f, true)]
|
||||
public float AllowedWireDisconnectionsPerMinute
|
||||
{
|
||||
get { return allowedWireDisconnectionsPerMinute; }
|
||||
set { allowedWireDisconnectionsPerMinute = Math.Max(0.0f, value); }
|
||||
}
|
||||
|
||||
[Serialize(6.0f, true)]
|
||||
public float WireDisconnectionKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(0.15f, true)]
|
||||
public float SteerSubKarmaIncrease { get; set; }
|
||||
|
||||
[Serialize(15.0f, true)]
|
||||
public float SpamFilterKarmaDecrease { get; set; }
|
||||
|
||||
[Serialize(40.0f, true)]
|
||||
public float HerpesThreshold { get; set; }
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
public float KickBanThreshold { get; set; }
|
||||
|
||||
[Serialize(10.0f, true)]
|
||||
public float KarmaNotificationInterval { get; set; }
|
||||
|
||||
private readonly AfflictionPrefab herpesAffliction;
|
||||
|
||||
public Dictionary<string, XElement> Presets = new Dictionary<string, XElement>();
|
||||
|
||||
public KarmaManager()
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(ConfigFile);
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, doc?.Root);
|
||||
if (doc?.Root != null)
|
||||
{
|
||||
Presets["custom"] = doc.Root;
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
string presetName = subElement.GetAttributeString("name", "");
|
||||
Presets[presetName.ToLowerInvariant()] = subElement;
|
||||
}
|
||||
SelectPreset("default");
|
||||
}
|
||||
herpesAffliction = AfflictionPrefab.List.Find(ap => ap.Identifier == "spaceherpes");
|
||||
}
|
||||
|
||||
public void SelectPreset(string presetName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(presetName)) { return; }
|
||||
presetName = presetName.ToLowerInvariant();
|
||||
|
||||
if (Presets.ContainsKey(presetName))
|
||||
{
|
||||
SerializableProperty.DeserializeProperties(this, Presets[presetName]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveCustomPreset()
|
||||
{
|
||||
if (Presets.ContainsKey("custom"))
|
||||
{
|
||||
SerializableProperty.SerializeProperties(this, Presets["custom"]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
XDocument doc = new XDocument(new XElement(Name));
|
||||
|
||||
foreach (KeyValuePair<string, XElement> preset in Presets)
|
||||
{
|
||||
doc.Root.Add(preset.Value);
|
||||
}
|
||||
|
||||
XmlWriterSettings settings = new XmlWriterSettings
|
||||
{
|
||||
Indent = true,
|
||||
NewLineOnAttributes = true
|
||||
};
|
||||
|
||||
using (var writer = XmlWriter.Create(ConfigFile, settings))
|
||||
{
|
||||
doc.Save(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,8 @@ namespace Barotrauma.Networking
|
||||
ApplyStatusEffect,
|
||||
ChangeProperty,
|
||||
Control,
|
||||
UpdateSkills
|
||||
UpdateSkills,
|
||||
Combine
|
||||
}
|
||||
|
||||
public readonly Entity Entity;
|
||||
|
||||
@@ -35,7 +35,8 @@ namespace Barotrauma.Networking
|
||||
CHAT_MESSAGE, //also self-explanatory
|
||||
VOTE, //you get the idea
|
||||
CHARACTER_INPUT,
|
||||
ENTITY_STATE
|
||||
ENTITY_STATE,
|
||||
SPECTATING_POS
|
||||
}
|
||||
|
||||
enum ClientNetError
|
||||
@@ -168,7 +169,13 @@ namespace Barotrauma.Networking
|
||||
updateInterval = new TimeSpan(0, 0, 0, 0, MathHelper.Clamp(1000 / serverSettings.TickRate, 1, 500));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public KarmaManager KarmaManager
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new KarmaManager();
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
@@ -214,7 +221,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
var radioComponent = radio.GetComponent<WifiComponent>();
|
||||
if (radioComponent == null) return false;
|
||||
return radioComponent.HasRequiredContainedItems(false);
|
||||
return radioComponent.HasRequiredContainedItems(sender, addMessage: false);
|
||||
}
|
||||
|
||||
public void AddChatMessage(string message, ChatMessageType type, string senderName = "", Character senderCharacter = null)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -18,10 +17,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
private NetworkMember networkMember;
|
||||
|
||||
private State state;
|
||||
|
||||
private Submarine respawnShuttle;
|
||||
private Steering shuttleSteering;
|
||||
private List<Door> shuttleDoors;
|
||||
|
||||
@@ -31,46 +26,40 @@ namespace Barotrauma.Networking
|
||||
|
||||
public bool UsingShuttle
|
||||
{
|
||||
get { return respawnShuttle != null; }
|
||||
get { return RespawnShuttle != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How long until the shuttle is dispatched with respawned characters
|
||||
/// When will the shuttle be dispatched with respawned characters
|
||||
/// </summary>
|
||||
public float RespawnTimer
|
||||
{
|
||||
get { return respawnTimer; }
|
||||
}
|
||||
public DateTime RespawnTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// how long until the shuttle starts heading back out of the level
|
||||
/// When will the sub start heading back out of the level
|
||||
/// </summary>
|
||||
public float TransportTimer
|
||||
{
|
||||
get { return shuttleTransportTimer; }
|
||||
}
|
||||
public DateTime ReturnTime { get; private set; }
|
||||
|
||||
public bool CountdownStarted
|
||||
public bool RespawnCountdownStarted
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public State CurrentState
|
||||
public bool ReturnCountdownStarted
|
||||
{
|
||||
get { return state; }
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private float respawnTimer, shuttleReturnTimer, shuttleTransportTimer;
|
||||
public State CurrentState { get; private set; }
|
||||
|
||||
private DateTime despawnTime;
|
||||
|
||||
private float maxTransportTime;
|
||||
|
||||
private float updateReturnTimer;
|
||||
|
||||
public Submarine RespawnShuttle
|
||||
{
|
||||
get { return respawnShuttle; }
|
||||
}
|
||||
public Submarine RespawnShuttle { get; private set; }
|
||||
|
||||
public RespawnManager(NetworkMember networkMember, Submarine shuttle)
|
||||
: base(shuttle)
|
||||
@@ -79,8 +68,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (shuttle != null)
|
||||
{
|
||||
respawnShuttle = new Submarine(shuttle.FilePath, shuttle.MD5Hash.Hash, true);
|
||||
respawnShuttle.Load(false);
|
||||
RespawnShuttle = new Submarine(shuttle.FilePath, shuttle.MD5Hash.Hash, true);
|
||||
RespawnShuttle.Load(false);
|
||||
|
||||
ResetShuttle();
|
||||
|
||||
@@ -89,7 +78,7 @@ namespace Barotrauma.Networking
|
||||
shuttleDoors = new List<Door>();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine != respawnShuttle) continue;
|
||||
if (item.Submarine != RespawnShuttle) continue;
|
||||
|
||||
var steering = item.GetComponent<Steering>();
|
||||
if (steering != null) shuttleSteering = steering;
|
||||
@@ -113,13 +102,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
respawnShuttle = null;
|
||||
RespawnShuttle = null;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (networkMember is GameServer server)
|
||||
{
|
||||
respawnTimer = server.ServerSettings.RespawnInterval;
|
||||
maxTransportTime = server.ServerSettings.MaxTransportTime;
|
||||
}
|
||||
#endif
|
||||
@@ -127,15 +115,15 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (respawnShuttle == null)
|
||||
if (RespawnShuttle == null)
|
||||
{
|
||||
if (state != State.Waiting)
|
||||
if (CurrentState != State.Waiting)
|
||||
{
|
||||
state = State.Waiting;
|
||||
CurrentState = State.Waiting;
|
||||
}
|
||||
}
|
||||
|
||||
switch (state)
|
||||
switch (CurrentState)
|
||||
{
|
||||
case State.Waiting:
|
||||
UpdateWaiting(deltaTime);
|
||||
@@ -155,32 +143,25 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
//infinite transport time -> shuttle wont return
|
||||
if (maxTransportTime <= 0.0f) return;
|
||||
|
||||
shuttleTransportTimer -= deltaTime;
|
||||
|
||||
UpdateTransportingProjSpecific(deltaTime);
|
||||
}
|
||||
|
||||
partial void UpdateTransportingProjSpecific(float deltaTime);
|
||||
|
||||
public void ForceRespawn()
|
||||
{
|
||||
ResetShuttle();
|
||||
RespawnTime = DateTime.Now;
|
||||
CurrentState = State.Waiting;
|
||||
}
|
||||
|
||||
private void UpdateReturning(float deltaTime)
|
||||
{
|
||||
//if (shuttleReturnTimer == maxTransportTime &&
|
||||
// networkMember.Character != null &&
|
||||
// networkMember.Character.Submarine == respawnShuttle)
|
||||
//{
|
||||
// networkMember.AddChatMessage("The shuttle will automatically return back to the outpost. Please leave the shuttle immediately.", ChatMessageType.Server);
|
||||
//}
|
||||
|
||||
shuttleReturnTimer -= deltaTime;
|
||||
|
||||
updateReturnTimer += deltaTime;
|
||||
|
||||
if (updateReturnTimer > 1.0f)
|
||||
{
|
||||
updateReturnTimer = 0.0f;
|
||||
|
||||
respawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
RespawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
|
||||
if (shuttleSteering != null)
|
||||
{
|
||||
@@ -196,41 +177,41 @@ namespace Barotrauma.Networking
|
||||
|
||||
private IEnumerable<object> ForceShuttleToPos(Vector2 position, float speed)
|
||||
{
|
||||
if (respawnShuttle == null)
|
||||
if (RespawnShuttle == null)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
respawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
RespawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
|
||||
while (Math.Abs(position.Y - respawnShuttle.WorldPosition.Y) > 100.0f)
|
||||
while (Math.Abs(position.Y - RespawnShuttle.WorldPosition.Y) > 100.0f)
|
||||
{
|
||||
Vector2 diff = position - respawnShuttle.WorldPosition;
|
||||
Vector2 diff = position - RespawnShuttle.WorldPosition;
|
||||
if (diff.LengthSquared() > 0.01f)
|
||||
{
|
||||
Vector2 displayVel = Vector2.Normalize(diff) * speed;
|
||||
respawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel);
|
||||
RespawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel);
|
||||
}
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
if (respawnShuttle.SubBody == null) yield return CoroutineStatus.Success;
|
||||
if (RespawnShuttle.SubBody == null) yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
respawnShuttle.PhysicsBody.FarseerBody.RestoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
RespawnShuttle.PhysicsBody.FarseerBody.RestoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private void ResetShuttle()
|
||||
{
|
||||
shuttleTransportTimer = maxTransportTime;
|
||||
shuttleReturnTimer = maxTransportTime;
|
||||
ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(maxTransportTime * 1000));
|
||||
despawnTime = ReturnTime + new TimeSpan(0, 0, seconds: 30);
|
||||
|
||||
if (respawnShuttle == null) return;
|
||||
if (RespawnShuttle == null) return;
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine != respawnShuttle) continue;
|
||||
if (item.Submarine != RespawnShuttle) continue;
|
||||
|
||||
//remove respawn items that have been left in the shuttle
|
||||
if (respawnItems.Contains(item))
|
||||
@@ -251,7 +232,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine != respawnShuttle) continue;
|
||||
if (wall.Submarine != RespawnShuttle) continue;
|
||||
|
||||
for (int i = 0; i < wall.SectionCount; i++)
|
||||
{
|
||||
@@ -259,12 +240,12 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == respawnShuttle && g.ConnectedWall != null);
|
||||
var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == RespawnShuttle && g.ConnectedWall != null);
|
||||
shuttleGaps.ForEach(g => Spawner.AddToRemoveQueue(g));
|
||||
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine != respawnShuttle) continue;
|
||||
if (hull.Submarine != RespawnShuttle) continue;
|
||||
|
||||
hull.OxygenPercentage = 100.0f;
|
||||
hull.WaterVolume = 0.0f;
|
||||
@@ -272,7 +253,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.Submarine != respawnShuttle) continue;
|
||||
if (c.Submarine != RespawnShuttle) continue;
|
||||
|
||||
#if CLIENT
|
||||
if (Character.Controlled == c) Character.Controlled = null;
|
||||
@@ -288,15 +269,12 @@ namespace Barotrauma.Networking
|
||||
if (item == null) continue;
|
||||
Spawner.AddToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
respawnShuttle.SetPosition(new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + respawnShuttle.Borders.Height));
|
||||
|
||||
respawnShuttle.Velocity = Vector2.Zero;
|
||||
|
||||
respawnShuttle.PhysicsBody.FarseerBody.RestoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
|
||||
RespawnShuttle.SetPosition(new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + RespawnShuttle.Borders.Height));
|
||||
RespawnShuttle.Velocity = Vector2.Zero;
|
||||
RespawnShuttle.PhysicsBody.FarseerBody.RestoreCollisionWith(Level.Loaded.TopBarrier);
|
||||
}
|
||||
|
||||
partial void RespawnCharactersProjSpecific();
|
||||
@@ -305,55 +283,92 @@ namespace Barotrauma.Networking
|
||||
RespawnCharactersProjSpecific();
|
||||
}
|
||||
|
||||
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
||||
public Vector2 FindSpawnPos()
|
||||
{
|
||||
msg.WriteRangedInteger(0, Enum.GetNames(typeof(State)).Length, (int)state);
|
||||
if (Level.Loaded == null || Submarine.MainSub == null) { return Vector2.Zero; }
|
||||
|
||||
switch (state)
|
||||
Rectangle dockedBorders = RespawnShuttle.GetDockedBorders();
|
||||
Vector2 diffFromDockedBorders =
|
||||
new Vector2(dockedBorders.Center.X, dockedBorders.Y - dockedBorders.Height / 2)
|
||||
- new Vector2(RespawnShuttle.Borders.Center.X, RespawnShuttle.Borders.Y - RespawnShuttle.Borders.Height / 2);
|
||||
|
||||
int minWidth = Math.Max(dockedBorders.Width, 1000);
|
||||
int minHeight = Math.Max(dockedBorders.Height, 1000);
|
||||
|
||||
List<Level.InterestingPosition> potentialSpawnPositions = new List<Level.InterestingPosition>();
|
||||
foreach (Level.InterestingPosition potentialSpawnPos in Level.Loaded.PositionsOfInterest.Where(p => p.PositionType == Level.PositionType.MainPath))
|
||||
{
|
||||
case State.Transporting:
|
||||
msg.Write(TransportTimer);
|
||||
break;
|
||||
case State.Waiting:
|
||||
msg.Write(CountdownStarted);
|
||||
msg.Write(respawnTimer);
|
||||
break;
|
||||
case State.Returning:
|
||||
break;
|
||||
}
|
||||
bool invalid = false;
|
||||
//make sure the shuttle won't overlap with any ruins
|
||||
foreach (var ruin in Level.Loaded.Ruins)
|
||||
{
|
||||
if (Math.Abs(ruin.Area.Center.X - potentialSpawnPos.Position.X) < (minWidth + ruin.Area.Width) / 2) { invalid = true; break; }
|
||||
if (Math.Abs(ruin.Area.Center.Y - potentialSpawnPos.Position.Y) < (minHeight + ruin.Area.Height) / 2) { invalid = true; break; }
|
||||
}
|
||||
if (invalid) { continue; }
|
||||
|
||||
msg.WritePadBits();
|
||||
}
|
||||
//make sure there aren't any walls too close
|
||||
var tooCloseCells = Level.Loaded.GetTooCloseCells(potentialSpawnPos.Position.ToVector2(), Math.Max(minWidth, minHeight));
|
||||
if (tooCloseCells.Any()) { continue; }
|
||||
|
||||
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||
{
|
||||
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
|
||||
//make sure the spawnpoint is far enough from other subs
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub == RespawnShuttle || RespawnShuttle.DockedTo.Contains(sub)) { continue; }
|
||||
|
||||
switch (newState)
|
||||
{
|
||||
case State.Transporting:
|
||||
maxTransportTime = msg.ReadSingle();
|
||||
shuttleTransportTimer = maxTransportTime;
|
||||
CountdownStarted = false;
|
||||
|
||||
if (state != newState)
|
||||
float minDist = Math.Max(Math.Max(minWidth, minHeight) + Math.Max(sub.Borders.Width, sub.Borders.Height), 10000.0f);
|
||||
if (Vector2.DistanceSquared(sub.WorldPosition, potentialSpawnPos.Position.ToVector2()) < minDist * minDist)
|
||||
{
|
||||
CoroutineManager.StopCoroutines("forcepos");
|
||||
CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f), "forcepos");
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case State.Waiting:
|
||||
CountdownStarted = msg.ReadBoolean();
|
||||
ResetShuttle();
|
||||
respawnTimer = msg.ReadSingle();
|
||||
break;
|
||||
case State.Returning:
|
||||
CountdownStarted = false;
|
||||
break;
|
||||
}
|
||||
state = newState;
|
||||
}
|
||||
if (invalid) { continue; }
|
||||
|
||||
msg.ReadPadBits();
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.IsDead)
|
||||
{
|
||||
//cannot spawn directly over dead bodies
|
||||
if (Math.Abs(character.WorldPosition.X - potentialSpawnPos.Position.X) < minWidth) { invalid = true; break; }
|
||||
if (Math.Abs(character.WorldPosition.Y - potentialSpawnPos.Position.Y) < minHeight) { invalid = true; break; }
|
||||
}
|
||||
else
|
||||
{
|
||||
//cannot spawn near alive characters (to prevent other players from seeing the shuttle
|
||||
//appear out of nowhere, or monsters from immediatelly wrecking the shuttle)
|
||||
if (Vector2.DistanceSquared(character.WorldPosition, potentialSpawnPos.Position.ToVector2()) < 5000.0f * 5000.0f)
|
||||
{
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invalid) { continue; }
|
||||
|
||||
potentialSpawnPositions.Add(potentialSpawnPos);
|
||||
}
|
||||
Vector2 bestSpawnPos = new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + RespawnShuttle.Borders.Height);
|
||||
float bestSpawnPosValue = 0.0f;
|
||||
foreach (var potentialSpawnPos in potentialSpawnPositions)
|
||||
{
|
||||
//the closer the spawnpos is to the main sub, the better
|
||||
float spawnPosValue = 100000.0f / Math.Max(Vector2.Distance(potentialSpawnPos.Position.ToVector2(), Submarine.MainSub.WorldPosition), 1.0f);
|
||||
|
||||
//prefer spawnpoints that are at the left side of the sub (so the shuttle doesn't have to go backwards)
|
||||
if (potentialSpawnPos.Position.X > Submarine.MainSub.WorldPosition.X)
|
||||
{
|
||||
spawnPosValue *= 0.1f;
|
||||
}
|
||||
|
||||
if (spawnPosValue > bestSpawnPosValue)
|
||||
{
|
||||
bestSpawnPos = potentialSpawnPos.Position.ToVector2();
|
||||
bestSpawnPosValue = spawnPosValue;
|
||||
}
|
||||
}
|
||||
|
||||
return bestSpawnPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma.Networking
|
||||
private SerializableProperty property;
|
||||
private string typeString;
|
||||
|
||||
private ServerSettings serverSettings;
|
||||
private object parentObject;
|
||||
|
||||
public string Name
|
||||
{
|
||||
@@ -103,16 +103,42 @@ namespace Barotrauma.Networking
|
||||
|
||||
public object Value
|
||||
{
|
||||
get { return property.GetValue(serverSettings); }
|
||||
get { return property.GetValue(parentObject); }
|
||||
set { property.SetValue(parentObject, value); }
|
||||
}
|
||||
|
||||
public NetPropertyData(ServerSettings serverSettings, SerializableProperty property, string typeString)
|
||||
|
||||
public NetPropertyData(object parentObject, SerializableProperty property, string typeString)
|
||||
{
|
||||
this.property = property;
|
||||
this.typeString = typeString;
|
||||
this.serverSettings = serverSettings;
|
||||
this.parentObject = parentObject;
|
||||
}
|
||||
|
||||
|
||||
public bool PropEquals(object a, object b)
|
||||
{
|
||||
switch (typeString)
|
||||
{
|
||||
case "float":
|
||||
if (!(a is float?)) return false;
|
||||
if (!(b is float?)) return false;
|
||||
return MathUtils.NearlyEqual((float)a, (float)b);
|
||||
case "int":
|
||||
if (!(a is int?)) return false;
|
||||
if (!(b is int?)) return false;
|
||||
return (int)a == (int)b;
|
||||
case "bool":
|
||||
if (!(a is bool?)) return false;
|
||||
if (!(b is bool?)) return false;
|
||||
return (bool)a == (bool)b;
|
||||
case "Enum":
|
||||
if (!(a is Enum)) return false;
|
||||
if (!(b is Enum)) return false;
|
||||
return ((Enum)a).Equals((Enum)b);
|
||||
default:
|
||||
return a.ToString().Equals(b.ToString(), StringComparison.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(NetBuffer msg)
|
||||
{
|
||||
long oldPos = msg.Position;
|
||||
@@ -126,20 +152,24 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
case "float":
|
||||
if (size != 4) break;
|
||||
property.SetValue(serverSettings, msg.ReadFloat());
|
||||
property.SetValue(parentObject, msg.ReadFloat());
|
||||
return;
|
||||
case "int":
|
||||
if (size != 4) break;
|
||||
property.SetValue(parentObject, msg.ReadInt32());
|
||||
return;
|
||||
case "vector2":
|
||||
if (size != 8) break;
|
||||
x = msg.ReadFloat();
|
||||
y = msg.ReadFloat();
|
||||
property.SetValue(serverSettings, new Vector2(x, y));
|
||||
property.SetValue(parentObject, new Vector2(x, y));
|
||||
return;
|
||||
case "vector3":
|
||||
if (size != 12) break;
|
||||
x = msg.ReadFloat();
|
||||
y = msg.ReadFloat();
|
||||
z = msg.ReadFloat();
|
||||
property.SetValue(serverSettings, new Vector3(x, y, z));
|
||||
property.SetValue(parentObject, new Vector3(x, y, z));
|
||||
return;
|
||||
case "vector4":
|
||||
if (size != 16) break;
|
||||
@@ -147,7 +177,7 @@ namespace Barotrauma.Networking
|
||||
y = msg.ReadFloat();
|
||||
z = msg.ReadFloat();
|
||||
w = msg.ReadFloat();
|
||||
property.SetValue(serverSettings, new Vector4(x, y, z, w));
|
||||
property.SetValue(parentObject, new Vector4(x, y, z, w));
|
||||
return;
|
||||
case "color":
|
||||
if (size != 4) break;
|
||||
@@ -155,7 +185,7 @@ namespace Barotrauma.Networking
|
||||
g = msg.ReadByte();
|
||||
b = msg.ReadByte();
|
||||
a = msg.ReadByte();
|
||||
property.SetValue(serverSettings, new Color(r, g, b, a));
|
||||
property.SetValue(parentObject, new Color(r, g, b, a));
|
||||
return;
|
||||
case "rectangle":
|
||||
if (size != 16) break;
|
||||
@@ -163,12 +193,12 @@ namespace Barotrauma.Networking
|
||||
iy = msg.ReadInt32();
|
||||
width = msg.ReadInt32();
|
||||
height = msg.ReadInt32();
|
||||
property.SetValue(serverSettings, new Rectangle(ix, iy, width, height));
|
||||
property.SetValue(parentObject, new Rectangle(ix, iy, width, height));
|
||||
return;
|
||||
default:
|
||||
msg.Position = oldPos; //reset position to properly read the string
|
||||
string incVal = msg.ReadString();
|
||||
property.TrySetValue(serverSettings, incVal);
|
||||
property.TrySetValue(parentObject, incVal);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -178,13 +208,17 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Write(NetBuffer msg, object overrideValue = null)
|
||||
{
|
||||
if (overrideValue == null) overrideValue = property.GetValue(serverSettings);
|
||||
if (overrideValue == null) overrideValue = property.GetValue(parentObject);
|
||||
switch (typeString)
|
||||
{
|
||||
case "float":
|
||||
msg.WriteVariableUInt32(4);
|
||||
msg.Write((float)overrideValue);
|
||||
break;
|
||||
case "int":
|
||||
msg.WriteVariableUInt32(4);
|
||||
msg.Write((int)overrideValue);
|
||||
break;
|
||||
case "vector2":
|
||||
msg.WriteVariableUInt32(8);
|
||||
msg.Write(((Vector2)overrideValue).X);
|
||||
@@ -232,14 +266,14 @@ namespace Barotrauma.Networking
|
||||
private set;
|
||||
}
|
||||
|
||||
Dictionary<UInt32,NetPropertyData> netProperties;
|
||||
Dictionary<UInt32, NetPropertyData> netProperties;
|
||||
|
||||
partial void InitProjSpecific();
|
||||
|
||||
public ServerSettings(string serverName, int port, int queryPort, int maxPlayers, bool isPublic, bool enableUPnP)
|
||||
{
|
||||
public ServerSettings(NetworkMember networkMember, string serverName, int port, int queryPort, int maxPlayers, bool isPublic, bool enableUPnP)
|
||||
{
|
||||
ServerLog = new ServerLog(serverName);
|
||||
|
||||
|
||||
Voting = new Voting();
|
||||
|
||||
Whitelist = new WhiteList();
|
||||
@@ -265,17 +299,30 @@ namespace Barotrauma.Networking
|
||||
foreach (var property in saveProperties)
|
||||
{
|
||||
object value = property.GetValue(this);
|
||||
if (value == null) continue;
|
||||
if (value == null) { continue; }
|
||||
|
||||
string typeName = SerializableProperty.GetSupportedTypeName(value.GetType());
|
||||
if (typeName != null || property.PropertyType.IsEnum)
|
||||
{
|
||||
NetPropertyData netPropertyData = new NetPropertyData(this, property, typeName);
|
||||
|
||||
UInt32 key = ToolBox.StringToUInt32Hash(property.Name, md5);
|
||||
if (netProperties.ContainsKey(key)){ throw new Exception("Hashing collision in ServerSettings.netProperties: " + netProperties[key] + " has same key as " + property.Name + " (" + key.ToString() + ")"); }
|
||||
netProperties.Add(key, netPropertyData);
|
||||
}
|
||||
}
|
||||
|
||||
if (netProperties.ContainsKey(key)) throw new Exception("Hashing collision in ServerSettings.netProperties: " + netProperties[key] + " has same key as " + property.Name + " (" + key.ToString() + ")");
|
||||
var karmaProperties = SerializableProperty.GetProperties<Serialize>(networkMember.KarmaManager);
|
||||
foreach (var property in karmaProperties)
|
||||
{
|
||||
object value = property.GetValue(networkMember.KarmaManager);
|
||||
if (value == null) { continue; }
|
||||
|
||||
string typeName = SerializableProperty.GetSupportedTypeName(value.GetType());
|
||||
if (typeName != null || property.PropertyType.IsEnum)
|
||||
{
|
||||
NetPropertyData netPropertyData = new NetPropertyData(networkMember.KarmaManager, property, typeName);
|
||||
UInt32 key = ToolBox.StringToUInt32Hash(property.Name, md5);
|
||||
if (netProperties.ContainsKey(key)) { throw new Exception("Hashing collision in ServerSettings.netProperties: " + netProperties[key] + " has same key as " + property.Name + " (" + key.ToString() + ")"); }
|
||||
netProperties.Add(key, netPropertyData);
|
||||
}
|
||||
}
|
||||
@@ -548,7 +595,14 @@ namespace Barotrauma.Networking
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
[Serialize(true, true)]
|
||||
public bool AllowFriendlyFire
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private YesNoMaybe traitorsEnabled;
|
||||
public YesNoMaybe TraitorsEnabled
|
||||
{
|
||||
@@ -631,12 +685,26 @@ namespace Barotrauma.Networking
|
||||
private set;
|
||||
}
|
||||
|
||||
private bool karmaEnabled;
|
||||
[Serialize(false, true)]
|
||||
public bool KarmaEnabled
|
||||
{
|
||||
get { return karmaEnabled; }
|
||||
set
|
||||
{
|
||||
karmaEnabled = value;
|
||||
#if CLIENT
|
||||
if (karmaSettingsBlocker != null) { karmaSettingsBlocker.Visible = !karmaEnabled || karmaPresetDD.SelectedData as string != "custom"; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("default", true)]
|
||||
public string KarmaPreset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
} = "default";
|
||||
|
||||
[Serialize("sandbox", true)]
|
||||
public string GameModeIdentifier
|
||||
@@ -664,14 +732,14 @@ namespace Barotrauma.Networking
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(60f, true)]
|
||||
[Serialize(60f * 60.0f, true)]
|
||||
public float AutoBanTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(360f, true)]
|
||||
[Serialize(60.0f * 60.0f * 24.0f, true)]
|
||||
public float MaxAutoBanTime
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -512,11 +512,9 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool IsValidValue(float value, string valueName, float? minValue = null, float? maxValue = null)
|
||||
public bool IsValidValue(float value, string valueName, float minValue = float.MinValue, float maxValue = float.MaxValue)
|
||||
{
|
||||
if (!MathUtils.IsValid(value) ||
|
||||
(minValue.HasValue && value < minValue.Value) ||
|
||||
(maxValue.HasValue && value > maxValue.Value))
|
||||
if (!MathUtils.IsValid(value) || value < minValue || value > maxValue)
|
||||
{
|
||||
string userData = UserData == null ? "null" : UserData.ToString();
|
||||
string errorMsg =
|
||||
@@ -539,11 +537,11 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsValidValue(Vector2 value, string valueName, float? minValue = null, float? maxValue = null)
|
||||
private bool IsValidValue(Vector2 value, string valueName, float minValue = float.MinValue, float maxValue = float.MaxValue)
|
||||
{
|
||||
if (!MathUtils.IsValid(value) ||
|
||||
(minValue.HasValue && (value.X < minValue.Value || value.Y < minValue.Value)) ||
|
||||
(maxValue.HasValue && (value.X > maxValue.Value || value.Y > maxValue)))
|
||||
(value.X < minValue || value.Y < minValue) ||
|
||||
(value.X > maxValue || value.Y > maxValue))
|
||||
{
|
||||
string userData = UserData == null ? "null" : UserData.ToString();
|
||||
string errorMsg =
|
||||
|
||||
@@ -7,12 +7,18 @@ namespace Barotrauma
|
||||
{
|
||||
partial class GameScreen : Screen
|
||||
{
|
||||
private Camera cam;
|
||||
private readonly Camera cam;
|
||||
|
||||
public override Camera Cam
|
||||
{
|
||||
get { return cam; }
|
||||
}
|
||||
|
||||
public double GameTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public GameScreen()
|
||||
{
|
||||
@@ -74,6 +80,9 @@ namespace Barotrauma
|
||||
closestSub.ApplyForce(targetMovement * closestSub.SubBody.Body.Mass * 100.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
GameTime += deltaTime;
|
||||
|
||||
foreach (PhysicsBody body in PhysicsBody.List)
|
||||
{
|
||||
body.Update((float)deltaTime);
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace Barotrauma
|
||||
public static string ParseContentPathFromUri(this XObject element)
|
||||
{
|
||||
string[] splitted = element.BaseUri.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
|
||||
IEnumerable<string> filtered = splitted.SkipWhile(part => part != "Content");
|
||||
string currentFolder = Environment.CurrentDirectory.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }).Last();
|
||||
// Filter out the current folder -> result is "Content/blaahblaah" or "Mods/blaahblaah" etc.
|
||||
IEnumerable<string> filtered = splitted.SkipWhile(part => part != currentFolder).Skip(1);
|
||||
return string.Join("/", filtered);
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,8 @@ namespace Barotrauma
|
||||
|
||||
private List<ItemSpawnInfo> spawnItems;
|
||||
|
||||
private Character user;
|
||||
|
||||
public readonly float FireSize;
|
||||
|
||||
public HashSet<string> TargetIdentifiers
|
||||
@@ -494,6 +496,7 @@ namespace Barotrauma
|
||||
|
||||
public void SetUser(Character user)
|
||||
{
|
||||
this.user = user;
|
||||
foreach (Affliction affliction in Afflictions)
|
||||
{
|
||||
affliction.Source = user;
|
||||
@@ -627,7 +630,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (explosion != null && entity != null) explosion.Explode(entity.WorldPosition, entity);
|
||||
if (explosion != null && entity != null) { explosion.Explode(entity.WorldPosition, damageSource: entity, attacker: user); }
|
||||
|
||||
foreach (ISerializableEntity target in targets)
|
||||
{
|
||||
@@ -655,14 +658,26 @@ namespace Barotrauma
|
||||
foreach (Pair<string, float> reduceAffliction in ReduceAffliction)
|
||||
{
|
||||
float reduceAmount = disableDeltaTime ? reduceAffliction.Second : reduceAffliction.Second * deltaTime;
|
||||
Limb targetLimb = null;
|
||||
Character targetCharacter = null;
|
||||
if (target is Character character)
|
||||
{
|
||||
character.CharacterHealth.ReduceAffliction(null, reduceAffliction.First, reduceAmount);
|
||||
targetCharacter = character;
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
limb.character.CharacterHealth.ReduceAffliction(limb, reduceAffliction.First, reduceAmount);
|
||||
targetLimb = limb;
|
||||
targetCharacter = limb.character;
|
||||
}
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
float prevVitality = targetCharacter.Vitality;
|
||||
targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAmount);
|
||||
#if SERVER
|
||||
GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, user, prevVitality - targetCharacter.Vitality);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,13 +837,24 @@ namespace Barotrauma
|
||||
|
||||
foreach (Pair<string, float> reduceAffliction in element.Parent.ReduceAffliction)
|
||||
{
|
||||
if (target is Character)
|
||||
Limb targetLimb = null;
|
||||
Character targetCharacter = null;
|
||||
if (target is Character character)
|
||||
{
|
||||
((Character)target).CharacterHealth.ReduceAffliction(null, reduceAffliction.First, reduceAffliction.Second * deltaTime);
|
||||
targetCharacter = character;
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
limb.character.CharacterHealth.ReduceAffliction(limb, reduceAffliction.First, reduceAffliction.Second * deltaTime);
|
||||
targetLimb = limb;
|
||||
targetCharacter = limb.character;
|
||||
}
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
float prevVitality = targetCharacter.Vitality;
|
||||
targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAffliction.Second * deltaTime);
|
||||
#if SERVER
|
||||
GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, element.Parent.user, prevVitality - targetCharacter.Vitality);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,17 @@ namespace Barotrauma
|
||||
//key = language
|
||||
private static Dictionary<string, List<TextPack>> textPacks = new Dictionary<string, List<TextPack>>();
|
||||
|
||||
private static string[] serverMessageCharacters = new string[] { "~", "[", "]", "=" };
|
||||
private static readonly string[] serverMessageCharacters = new string[] { "~", "[", "]", "=" };
|
||||
|
||||
public static string Language;
|
||||
|
||||
private static HashSet<string> availableLanguages = new HashSet<string>();
|
||||
public static bool Initialized
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> availableLanguages = new HashSet<string>();
|
||||
public static IEnumerable<string> AvailableLanguages
|
||||
{
|
||||
get { return availableLanguages; }
|
||||
@@ -99,6 +105,7 @@ namespace Barotrauma
|
||||
availableLanguages.Add(textPack.Language);
|
||||
textPacks.Add(textPack.Language, new List<TextPack>() { textPack });
|
||||
}
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
public static bool ContainsTag(string textTag)
|
||||
|
||||
@@ -260,19 +260,20 @@ namespace Barotrauma
|
||||
|
||||
public static string SecondsToReadableTime(float seconds)
|
||||
{
|
||||
int s = (int)(seconds % 60.0f);
|
||||
if (seconds < 60.0f)
|
||||
{
|
||||
return (int)seconds + " s";
|
||||
return s + " s";
|
||||
}
|
||||
else
|
||||
{
|
||||
int m = (int)(seconds / 60.0f);
|
||||
int s = (int)(seconds % 60.0f);
|
||||
|
||||
return s == 0 ?
|
||||
m + " m" :
|
||||
m + " m " + s + " s";
|
||||
}
|
||||
int h = (int)(seconds / (60.0f * 60.0f));
|
||||
int m = (int)((seconds / 60.0f) % 60);
|
||||
|
||||
string text = "";
|
||||
if (h != 0) { text = h + " h"; }
|
||||
if (m != 0) { text = string.IsNullOrEmpty(text) ? m + " m" : string.Join(" ", text, m, "m"); }
|
||||
if (s != 0) { text = string.IsNullOrEmpty(text) ? s + " s" : string.Join(" ", text, s, "s"); }
|
||||
return text;
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> cachedLines = new Dictionary<string, List<string>>();
|
||||
|
||||
Reference in New Issue
Block a user