v0.10.5.1
This commit is contained in:
@@ -5,6 +5,7 @@ using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -392,6 +393,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.GetComponent<LevelResource>() != null) { return true; }
|
||||
|
||||
if (item.GetComponent<Planter>() is { } planter && planter.GrowableSeeds.Any(seed => seed != null)) { return false; }
|
||||
|
||||
//if the item has a connection panel and rewiring is disabled, don't allow deattaching
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null && (connectionPanel.Locked || !(GameMain.NetworkMember?.ServerSettings?.AllowRewiring ?? true)))
|
||||
@@ -476,12 +479,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
var containedItems = item.ContainedItems;
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
if (containedItems != null)
|
||||
{
|
||||
foreach (Item contained in containedItems)
|
||||
{
|
||||
if (contained.body == null) continue;
|
||||
if (contained == null) { continue; }
|
||||
if (contained.body == null) { continue; }
|
||||
contained.SetTransform(item.SimPosition, contained.body.Rotation);
|
||||
}
|
||||
}
|
||||
@@ -573,7 +577,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (item.body == null || !item.body.Enabled) return;
|
||||
if (item.body == null || !item.body.Enabled) { return; }
|
||||
if (picker == null || !picker.HasEquippedItem(item))
|
||||
{
|
||||
if (Pusher != null) { Pusher.Enabled = false; }
|
||||
@@ -598,7 +602,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
|
||||
|
||||
if (item.body.Dir != picker.AnimController.Dir) Flip();
|
||||
if (item.body.Dir != picker.AnimController.Dir)
|
||||
{
|
||||
item.FlipX(relativeToSub: false);
|
||||
}
|
||||
|
||||
item.Submarine = picker.Submarine;
|
||||
|
||||
@@ -635,11 +642,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void Flip()
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
handlePos[0].X = -handlePos[0].X;
|
||||
handlePos[1].X = -handlePos[1].X;
|
||||
item.body.Dir = -item.body.Dir;
|
||||
if (item.body != null)
|
||||
{
|
||||
item.body.Dir = -item.body.Dir;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
|
||||
@@ -63,6 +63,13 @@ namespace Barotrauma.Items.Components
|
||||
item.RequireAimToUse = true;
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
base.Equip(character);
|
||||
reloadTimer = Math.Min(reload, 1.0f);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || reloadTimer > 0.0f) { return false; }
|
||||
@@ -151,7 +158,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
|
||||
|
||||
if (item.body.Dir != picker.AnimController.Dir) { Flip(); }
|
||||
if (item.body.Dir != picker.AnimController.Dir) { item.FlipX(relativeToSub: false); }
|
||||
|
||||
AnimController ac = picker.AnimController;
|
||||
|
||||
@@ -366,13 +373,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
bool success = Rand.Range(0.0f, 0.5f) < DegreeOfSuccess(User);
|
||||
|
||||
#if SERVER
|
||||
if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(item, new object[]
|
||||
{
|
||||
Networking.NetEntityEvent.Type.ApplyStatusEffect,
|
||||
ActionType.OnUse,
|
||||
success ? ActionType.OnUse : ActionType.OnFailure,
|
||||
null, //itemcomponent
|
||||
targetCharacter.ID, targetLimb
|
||||
});
|
||||
@@ -389,7 +398,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (targetCharacter != null) //TODO: Allow OnUse to happen on structures too maybe??
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb, user: User);
|
||||
ApplyStatusEffects(success ? ActionType.OnUse : ActionType.OnFailure, 1.0f, targetCharacter, targetLimb, user: User);
|
||||
}
|
||||
|
||||
if (DeleteOnUse)
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Barotrauma.Items.Components
|
||||
allowedSlots.Add(allowedSlot);
|
||||
}
|
||||
|
||||
canBePicked = true;
|
||||
canBePicked = true;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
@@ -142,7 +142,8 @@ namespace Barotrauma.Items.Components
|
||||
this,
|
||||
item.WorldPosition,
|
||||
pickTimer / requiredTime,
|
||||
GUI.Style.Red, GUI.Style.Green);
|
||||
GUI.Style.Red, GUI.Style.Green,
|
||||
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
|
||||
#endif
|
||||
|
||||
picker.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
|
||||
|
||||
@@ -72,6 +72,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
reloadTimer = Math.Min(reload, 1.0f);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
reloadTimer -= deltaTime;
|
||||
@@ -180,22 +186,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Projectile FindProjectile(bool triggerOnUseOnContainers = false)
|
||||
{
|
||||
var containedItems = item.ContainedItems;
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
if (containedItems == null) { return null; }
|
||||
|
||||
foreach (Item item in containedItems)
|
||||
{
|
||||
if (item == null) { continue; }
|
||||
Projectile projectile = item.GetComponent<Projectile>();
|
||||
if (projectile != null) { return projectile; }
|
||||
}
|
||||
|
||||
//projectile not found, see if one of the contained items contains projectiles
|
||||
foreach (Item item in containedItems)
|
||||
foreach (Item it in containedItems)
|
||||
{
|
||||
var containedSubItems = item.ContainedItems;
|
||||
if (it == null) { continue; }
|
||||
var containedSubItems = it.OwnInventory?.Items;
|
||||
if (containedSubItems == null) { continue; }
|
||||
foreach (Item subItem in containedSubItems)
|
||||
{
|
||||
if (subItem == null) { continue; }
|
||||
Projectile projectile = subItem.GetComponent<Projectile>();
|
||||
//apply OnUse statuseffects to the container in case it has to react to it somehow
|
||||
//(play a sound, spawn more projectiles, reduce condition...)
|
||||
|
||||
@@ -52,12 +52,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How much the item decreases the size of fires per second.")]
|
||||
public float ExtinguishAmount
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How much water the item provides to planters per second.")]
|
||||
public float WaterAmount { get; set; }
|
||||
|
||||
[Serialize("0.0,0.0", false, description: "The position of the barrel as an offset from the item's center (in pixels).")]
|
||||
public Vector2 BarrelPos { get; set; }
|
||||
|
||||
@@ -82,13 +86,19 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(0.0f, false, description: "Force applied to the entity the ray hits.")]
|
||||
public float TargetForce { get; set; }
|
||||
|
||||
[Serialize(0.0f, false, description: "Rotation of the barrel in degrees."), Editable(MinValueFloat = 0, MaxValueFloat = 360, VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" })]
|
||||
public float BarrelRotation
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Vector2 TransformedBarrelPos
|
||||
{
|
||||
get
|
||||
{
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation);
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation + MathHelper.ToRadians(BarrelRotation));
|
||||
Vector2 flippedPos = BarrelPos;
|
||||
if (item.body.Dir < 0.0f) flippedPos.X = -flippedPos.X;
|
||||
if (item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
|
||||
return (Vector2.Transform(flippedPos, bodyTransform));
|
||||
}
|
||||
}
|
||||
@@ -188,7 +198,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
float spread = MathHelper.ToRadians(MathHelper.Lerp(UnskilledSpread, Spread, degreeOfSuccess));
|
||||
float angle = item.body.Rotation + spread * Rand.Range(-0.5f, 0.5f);
|
||||
float angle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + spread * Rand.Range(-0.5f, 0.5f);
|
||||
Vector2 rayEnd = rayStart +
|
||||
ConvertUnits.ToSimUnits(new Vector2(
|
||||
(float)Math.Cos(angle),
|
||||
@@ -276,7 +286,7 @@ namespace Barotrauma.Items.Components
|
||||
ignoreSensors: false,
|
||||
customPredicate: (Fixture f) =>
|
||||
{
|
||||
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return false; }
|
||||
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure || (f.Body?.UserData is Item it && it.GetComponent<Planter>() != null)) { return false; }
|
||||
if (f.Body?.UserData as string == "ruinroom") { return false; }
|
||||
return true;
|
||||
},
|
||||
@@ -373,6 +383,42 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (WaterAmount > 0.0f && item.CurrentHull?.Submarine != null)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(rayStart + item.Submarine.SimPosition);
|
||||
|
||||
// Could probably be done much efficiently here
|
||||
foreach (Item it in Item.ItemList)
|
||||
{
|
||||
if (it.Submarine == item.Submarine && it.GetComponent<Planter>() is { } planter)
|
||||
{
|
||||
if (it.GetComponent<Holdable>() is { } holdable && holdable.Attachable && !holdable.Attached) { continue; }
|
||||
|
||||
Rectangle collisionRect = it.WorldRect;
|
||||
collisionRect.Y -= collisionRect.Height;
|
||||
if (collisionRect.Left < pos.X && collisionRect.Right > pos.X && collisionRect.Bottom < pos.Y)
|
||||
{
|
||||
Body collision = Submarine.PickBody(rayStart, it.SimPosition, ignoredBodies, collisionCategories);
|
||||
if (collision == null)
|
||||
{
|
||||
for (var i = 0; i < planter.GrowableSeeds.Length; i++)
|
||||
{
|
||||
Growable seed = planter.GrowableSeeds[i];
|
||||
if (seed == null || seed.Decayed) { continue; }
|
||||
|
||||
seed.Health += WaterAmount * deltaTime;
|
||||
|
||||
#if CLIENT
|
||||
float barOffset = 10f * GUI.Scale;
|
||||
Vector2 offset = planter.PlantSlots.ContainsKey(i) ? planter.PlantSlots[i].Offset : Vector2.Zero;
|
||||
user.UpdateHUDProgressBar(planter, planter.Item.DrawPosition + new Vector2(barOffset, 0) + offset, seed.Health / seed.MaxHealth, GUI.Style.Blue, GUI.Style.Blue, "progressbar.watering");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
@@ -464,7 +510,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (targetBody.UserData is Item targetItem)
|
||||
{
|
||||
if (!HitItems) { return false; }
|
||||
if (!HitItems || targetItem.NonInteractable) { return false; }
|
||||
|
||||
var levelResource = targetItem.GetComponent<LevelResource>();
|
||||
if (levelResource != null && levelResource.Attached &&
|
||||
@@ -477,8 +523,9 @@ namespace Barotrauma.Items.Components
|
||||
this,
|
||||
targetItem.WorldPosition,
|
||||
levelResource.DeattachTimer / levelResource.DeattachDuration,
|
||||
GUI.Style.Red, GUI.Style.Green);
|
||||
GUI.Style.Red, GUI.Style.Green, "progressbar.deattaching");
|
||||
#endif
|
||||
FixItemProjSpecific(user, deltaTime, targetItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -571,34 +618,31 @@ namespace Barotrauma.Items.Components
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (dist < reach / 2)
|
||||
{
|
||||
if (dist < reach / 2)
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
|
||||
}
|
||||
else if (dist < reach * 2)
|
||||
{
|
||||
// In or almost in range
|
||||
character.CursorPosition = leak.Position;
|
||||
character.CursorPosition += VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
|
||||
}
|
||||
else if (dist <= reach)
|
||||
{
|
||||
// In range
|
||||
character.CursorPosition = leak.Position;
|
||||
character.CursorPosition += VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
var torso = character.AnimController.GetLimb(LimbType.Torso);
|
||||
// Turn facing the target when not moving (handled in the animcontroller if not moving)
|
||||
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
|
||||
Vector2 diff = (mousePos - torso.SimPosition) * character.AnimController.Dir;
|
||||
float newRotation = MathUtils.VectorToAngle(diff);
|
||||
character.AnimController.Collider.SmoothRotate(newRotation, 5.0f);
|
||||
var torso = character.AnimController.GetLimb(LimbType.Torso);
|
||||
// Turn facing the target when not moving (handled in the animcontroller if not moving)
|
||||
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
|
||||
Vector2 diff = (mousePos - torso.SimPosition) * character.AnimController.Dir;
|
||||
float newRotation = MathUtils.VectorToAngle(diff);
|
||||
character.AnimController.Collider.SmoothRotate(newRotation, 5.0f);
|
||||
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4)
|
||||
{
|
||||
// Swim past
|
||||
Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX;
|
||||
moveDir *= character.AnimController.Dir;
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir);
|
||||
}
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4)
|
||||
{
|
||||
// Swim past
|
||||
Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX;
|
||||
moveDir *= character.AnimController.Dir;
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -674,9 +718,8 @@ namespace Barotrauma.Items.Components
|
||||
// A general purpose system could be better, but it would most likely require changes in the way we define the status effects in xml.
|
||||
foreach (ISerializableEntity target in targets)
|
||||
{
|
||||
if (!(target is Door door)) { continue; }
|
||||
|
||||
if (!door.CanBeWelded) { continue; }
|
||||
if (!(target is Door door)) { continue; }
|
||||
if (!door.CanBeWelded || door.Item.NonInteractable) { continue; }
|
||||
for (int i = 0; i < effect.propertyNames.Length; i++)
|
||||
{
|
||||
string propertyName = effect.propertyNames[i];
|
||||
@@ -685,7 +728,7 @@ namespace Barotrauma.Items.Components
|
||||
object value = property.GetValue(target);
|
||||
if (door.Stuck > 0)
|
||||
{
|
||||
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White);
|
||||
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White, "progressbar.welding");
|
||||
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Sprayer : RangedWeapon
|
||||
{
|
||||
[Serialize(0.0f, false, description: "The distance at which the item can spray walls.")]
|
||||
public float Range { get; set; }
|
||||
|
||||
[Serialize(1.0f, false, description: "How fast the item changes the color of the walls.")]
|
||||
public float SprayStrength { get; set; }
|
||||
|
||||
private readonly Dictionary<string, Color> liquidColors;
|
||||
private ItemContainer liquidContainer;
|
||||
|
||||
public Sprayer(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
item.IsShootable = true;
|
||||
item.RequireAimToUse = true;
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "paintcolors":
|
||||
{
|
||||
liquidColors = new Dictionary<string, Color>();
|
||||
foreach (XElement paintElement in subElement.Elements())
|
||||
{
|
||||
string paintName = paintElement.GetAttributeString("paintitem", string.Empty);
|
||||
Color paintColor = paintElement.GetAttributeColor("color", Color.Transparent);
|
||||
|
||||
if (paintName != string.Empty)
|
||||
{
|
||||
liquidColors.Add(paintName, paintColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
liquidContainer = item.GetComponent<ItemContainer>();
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
|
||||
#if SERVER
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
return character != null || character.Removed;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
|
||||
|
||||
if (item.body.Dir != picker.AnimController.Dir) { Flip(); }
|
||||
if (item.body.Dir != picker.AnimController.Dir) { item.FlipX(relativeToSub: false); }
|
||||
|
||||
AnimController ac = picker.AnimController;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user