Faction Test 100.4.0.0

This commit is contained in:
Markus Isberg
2022-11-14 18:28:28 +02:00
parent 87426b68b2
commit c772b61fc1
412 changed files with 16984 additions and 5530 deletions
@@ -127,7 +127,7 @@ namespace Barotrauma.Items.Components
set { attachedByDefault = value; }
}
[Editable, Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position the character holds the item at (in pixels, as an offset from the character's shoulder)."+
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position the character holds the item at (in pixels, as an offset from the character's shoulder)."+
" For example, a value of 10,-100 would make the character hold the item 100 pixels below the shoulder and 10 pixels forwards.")]
public Vector2 HoldPos
{
@@ -143,7 +143,11 @@ namespace Barotrauma.Items.Components
set { aimPos = ConvertUnits.ToSimUnits(value); }
}
#if DEBUG
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "The rotation at which the character holds the item (in degrees, relative to the rotation of the character's hand).")]
#else
[Serialize(0.0f, IsPropertySaveable.No)]
#endif
public float HoldAngle
{
get { return MathHelper.ToDegrees(holdAngle); }
@@ -151,23 +155,50 @@ namespace Barotrauma.Items.Components
}
private Vector2 swingAmount;
#if DEBUG
[Editable, Serialize("0.0,0.0", IsPropertySaveable.No, description: "How much the item swings around when aiming/holding it (in pixels, as an offset from AimPos/HoldPos).")]
#else
[Serialize("0.0,0.0", IsPropertySaveable.No)]
#endif
public Vector2 SwingAmount
{
get { return ConvertUnits.ToDisplayUnits(swingAmount); }
set { swingAmount = ConvertUnits.ToSimUnits(value); }
}
#if DEBUG
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How fast the item swings around when aiming/holding it (only valid if SwingAmount is set).")]
#else
[Serialize(0.0f, IsPropertySaveable.No)]
#endif
public float SwingSpeed { get; set; }
#if DEBUG
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the item swing around when it's being held.")]
#else
[Serialize(false, IsPropertySaveable.No)]
#endif
public bool SwingWhenHolding { get; set; }
#if DEBUG
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the item swing around when it's being aimed.")]
#else
[Serialize(false, IsPropertySaveable.No)]
#endif
public bool SwingWhenAiming { get; set; }
#if DEBUG
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the item swing around when it's being used (for example, when firing a weapon or a welding tool).")]
#else
[Serialize(false, IsPropertySaveable.No)]
#endif
public bool SwingWhenUsing { get; set; }
#if DEBUG
[Editable, Serialize(false, IsPropertySaveable.No)]
#else
[Serialize(false, IsPropertySaveable.No)]
#endif
public bool DisableHeadRotation { get; set; }
[ConditionallyEditable(ConditionallyEditable.ConditionType.Attachable, MinValueFloat = 0.0f, MaxValueFloat = 0.999f, DecimalCount = 3), Serialize(0.55f, IsPropertySaveable.No, description: "Sprite depth that's used when the item is NOT attached to a wall.")]
@@ -185,7 +216,7 @@ namespace Barotrauma.Items.Components
Pusher = null;
if (element.GetAttributeBool("blocksplayers", false))
{
Pusher = new PhysicsBody(item.body.width, item.body.height, item.body.radius,
Pusher = new PhysicsBody(item.body.Width, item.body.Height, item.body.Radius,
item.body.Density,
BodyType.Dynamic,
Physics.CollisionItemBlocking,
@@ -386,10 +417,11 @@ namespace Barotrauma.Items.Components
return;
}
//cannot hold and wear an item at the same time
//(unless the slot in which it's held and worn are equal - e.g. a suit with built-in tool or weapon on one hand)
var wearable = item.GetComponent<Wearable>();
if (wearable != null)
if (wearable != null && !wearable.AllowedSlots.SequenceEqual(allowedSlots))
{
//cannot hold and wear an item at the same time
wearable.Unequip(character);
}
@@ -650,11 +682,10 @@ namespace Barotrauma.Items.Components
return false;
}
Vector2 attachPos = GetAttachPosition(character, useWorldCoordinates: true);
Structure attachTarget = Structure.GetAttachTarget(attachPos);
Submarine attachSubmarine = Structure.GetAttachTarget(attachPos)?.Submarine ?? item.Submarine;
int maxAttachableCount = (int)character.Info.GetSavedStatValue(StatTypes.MaxAttachableCount, item.Prefab.Identifier);
int currentlyAttachedCount = Item.ItemList.Count(
i => i.Submarine == attachTarget?.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.Prefab.Identifier);
i => i.Submarine == attachSubmarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.Prefab.Identifier);
if (maxAttachableCount == 0)
{
#if CLIENT
@@ -812,10 +843,18 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (item.body == null || !item.body.Enabled) { return; }
Character owner = picker ?? item.GetRootInventoryOwner() as Character;
if (owner != null)
{
ApplyStatusEffects(ActionType.OnActive, deltaTime, owner);
}
if (picker == null || !picker.HasEquippedItem(item))
{
if (Pusher != null) { Pusher.Enabled = false; }
if (attachTargetCell == null) { IsActive = false; }
if (attachTargetCell == null && owner == null) { IsActive = false; }
return;
}
@@ -824,23 +863,7 @@ namespace Barotrauma.Items.Components
Drawable = true;
}
Vector2 swing = Vector2.Zero;
if (swingAmount != Vector2.Zero && !picker.IsUnconscious && picker.Stun <= 0.0f)
{
swingState += deltaTime;
swingState %= 1.0f;
if (SwingWhenHolding ||
(SwingWhenAiming && picker.IsKeyDown(InputType.Aim)) ||
(SwingWhenUsing && picker.IsKeyDown(InputType.Aim) && picker.IsKeyDown(InputType.Shoot)))
{
swing = swingAmount * new Vector2(
PerlinNoise.GetPerlin(swingState * SwingSpeed * 0.1f, swingState * SwingSpeed * 0.1f) - 0.5f,
PerlinNoise.GetPerlin(swingState * SwingSpeed * 0.1f + 0.5f, swingState * SwingSpeed * 0.1f + 0.5f) - 0.5f);
}
}
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
UpdateSwingPos(deltaTime, out Vector2 swingPos);
if (item.body.Dir != picker.AnimController.Dir)
{
item.FlipX(relativeToSub: false);
@@ -853,7 +876,7 @@ namespace Barotrauma.Items.Components
scaledHandlePos[0] = handlePos[0] * item.Scale;
scaledHandlePos[1] = handlePos[1] * item.Scale;
bool aim = picker.IsKeyDown(InputType.Aim) && aimPos != Vector2.Zero && picker.CanAim;
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swing, aimPos + swing, aim, holdAngle);
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swingPos, aimPos + swingPos, aim, holdAngle);
if (!aim)
{
var rope = GetRope();
@@ -890,6 +913,24 @@ namespace Barotrauma.Items.Components
}
}
public void UpdateSwingPos(float deltaTime, out Vector2 swingPos)
{
swingPos = Vector2.Zero;
if (swingAmount != Vector2.Zero && !picker.IsUnconscious && picker.Stun <= 0.0f)
{
swingState += deltaTime;
swingState %= 1.0f;
if (SwingWhenHolding ||
(SwingWhenAiming && picker.IsKeyDown(InputType.Aim)) ||
(SwingWhenUsing && picker.IsKeyDown(InputType.Aim) && picker.IsKeyDown(InputType.Shoot)))
{
swingPos = swingAmount * new Vector2(
PerlinNoise.GetPerlin(swingState * SwingSpeed * 0.1f, swingState * SwingSpeed * 0.1f) - 0.5f,
PerlinNoise.GetPerlin(swingState * SwingSpeed * 0.1f + 0.5f, swingState * SwingSpeed * 0.1f + 0.5f) - 0.5f);
}
}
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
//do nothing
@@ -128,7 +128,7 @@ namespace Barotrauma.Items.Components
if (body != null)
{
trigger = new PhysicsBody(body.width, body.height, body.radius,
trigger = new PhysicsBody(body.Width, body.Height, body.Radius,
body.Density,
BodyType.Static,
Physics.CollisionWall,
@@ -214,8 +214,9 @@ namespace Barotrauma.Items.Components
bool aim = item.RequireAimToUse && picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && picker.CanAim;
if (aim)
{
UpdateSwingPos(deltaTime, out Vector2 swingPos);
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 3f, MathHelper.PiOver4));
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos, aimMelee: true);
ac.HoldItem(deltaTime, item, handlePos, aimPos + swingPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos, aimMelee: true);
if (ac.InWater)
{
ac.LockFlippingUntil = (float)Timing.TotalTime + Reload;
@@ -392,36 +393,37 @@ namespace Barotrauma.Items.Components
float damageMultiplier = 1 + User.GetStatValue(StatTypes.MeleeAttackMultiplier);
damageMultiplier *= 1.0f + item.GetQualityModifier(Quality.StatType.StrikingPowerMultiplier);
Character user = User;
Limb targetLimb = target.UserData as Limb;
Character targetCharacter = targetLimb?.character ?? target.UserData as Character;
if (Attack != null)
{
Attack.SetUser(User);
Attack.SetUser(user);
Attack.DamageMultiplier = damageMultiplier;
if (targetLimb != null)
{
if (targetLimb.character.Removed) { return; }
targetLimb.character.LastDamageSource = item;
Attack.DoDamageToLimb(User, targetLimb, item.WorldPosition, 1.0f);
Attack.DoDamageToLimb(user, targetLimb, item.WorldPosition, 1.0f);
}
else if (targetCharacter != null)
{
if (targetCharacter.Removed) { return; }
targetCharacter.LastDamageSource = item;
Attack.DoDamage(User, targetCharacter, item.WorldPosition, 1.0f);
Attack.DoDamage(user, targetCharacter, item.WorldPosition, 1.0f);
}
else if ((target.UserData as Structure ?? targetFixture.UserData as Structure) is Structure targetStructure)
{
if (targetStructure.Removed) { return; }
Attack.DoDamage(User, targetStructure, item.WorldPosition, 1.0f);
Attack.DoDamage(user, targetStructure, item.WorldPosition, 1.0f);
}
else if (target.UserData is Item targetItem && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0)
{
if (targetItem.Removed) { return; }
var attackResult = Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
var attackResult = Attack.DoDamage(user, targetItem, item.WorldPosition, 1.0f);
#if CLIENT
if (attackResult.Damage > 0.0f)
if (attackResult.Damage > 0.0f && targetItem.Prefab.ShowHealthBar)
{
Character.Controlled?.UpdateHUDProgressBar(targetItem,
targetItem.WorldPosition,
@@ -435,7 +437,7 @@ namespace Barotrauma.Items.Components
else if (target.UserData is Holdable holdable && holdable.CanPush)
{
if (holdable.Item.Removed) { return; }
Attack.DoDamage(User, holdable.Item, item.WorldPosition, 1.0f);
Attack.DoDamage(user, holdable.Item, item.WorldPosition, 1.0f);
RestoreCollision();
hitting = false;
User = null;
@@ -448,29 +450,32 @@ 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
ActionType conditionalActionType = ActionType.OnSuccess;
if (user != null && Rand.Range(0.0f, 0.5f) > DegreeOfSuccess(user))
{
GameMain.Server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(
success ? ActionType.OnUse : ActionType.OnFailure,
targetItemComponent: null,
targetCharacter, targetLimb));
string logStr = picker?.LogName + " used " + item.Name;
if (item.ContainedItems != null && item.ContainedItems.Any())
{
logStr += " (" + string.Join(", ", item.ContainedItems.Select(i => i?.Name)) + ")";
}
logStr += " on " + targetCharacter.LogName + ".";
Networking.GameServer.Log(logStr, Networking.ServerLog.MessageType.Attack);
conditionalActionType = ActionType.OnFailure;
}
if (GameMain.NetworkMember is { IsServer: true } server && targetCharacter != null)
{
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(conditionalActionType, targetItemComponent: null, targetCharacter, targetLimb));
server.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnUse, targetItemComponent: null, targetCharacter, targetLimb));
#if SERVER
if (GameMain.Server != null) //TODO: Log structure hits
{
string logStr = picker?.LogName + " used " + item.Name;
if (item.ContainedItems != null && item.ContainedItems.Any())
{
logStr += " (" + string.Join(", ", item.ContainedItems.Select(i => i?.Name)) + ")";
}
logStr += " on " + targetCharacter.LogName + ".";
Networking.GameServer.Log(logStr, Networking.ServerLog.MessageType.Attack);
}
#endif
}
#endif
if (targetCharacter != null) //TODO: Allow OnUse to happen on structures too maybe??
{
ApplyStatusEffects(success ? ActionType.OnUse : ActionType.OnFailure, 1.0f, targetCharacter, targetLimb, user: User, afflictionMultiplier: damageMultiplier);
ApplyStatusEffects(conditionalActionType, 1.0f, targetCharacter, targetLimb, user: user, afflictionMultiplier: damageMultiplier);
ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb, user: user, afflictionMultiplier: damageMultiplier);
}
if (DeleteOnUse)
@@ -23,6 +23,8 @@ namespace Barotrauma.Items.Components
[Serialize(0.0f, IsPropertySaveable.No, description: "The force to apply to the user's body."), Editable(MinValueFloat = -1000.0f, MaxValueFloat = 1000.0f)]
public float Force { get; set; }
[Serialize(true, IsPropertySaveable.No, description: "If the item is held in RightHand or LeftHand, apply extra force there")]
public bool ApplyToHands { get; set; }
#if CLIENT
private string particles;
[Serialize("", IsPropertySaveable.No, description: "The name of the particle prefab the item emits when used.")]
@@ -70,13 +72,16 @@ namespace Barotrauma.Items.Components
character.AnimController.Collider.ApplyForce(propulsion);
if (character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand))
{
character.AnimController.GetLimb(LimbType.RightHand)?.body.ApplyForce(propulsion);
}
if (character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand))
{
character.AnimController.GetLimb(LimbType.LeftHand)?.body.ApplyForce(propulsion);
if (ApplyToHands)
{
if (character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand))
{
character.AnimController.GetLimb(LimbType.RightHand)?.body.ApplyForce(propulsion);
}
if (character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand))
{
character.AnimController.GetLimb(LimbType.LeftHand)?.body.ApplyForce(propulsion);
}
}
#if CLIENT
@@ -32,6 +32,20 @@ namespace Barotrauma.Items.Components
set { reload = Math.Max(value, 0.0f); }
}
[Serialize(0f, IsPropertySaveable.No, description: "Weapons skill requirement to reload at normal speed.")]
public float ReloadSkillRequirement
{
get;
set;
}
[Serialize(1.0f, IsPropertySaveable.No, description: "Reload time at 0 skill level. Reload time scales with skill level up to the Weapons skill requirement.")]
public float ReloadNoSkill
{
get;
set;
}
[Serialize(false, IsPropertySaveable.No, description: "Tells the AI to hold the trigger down when it uses this weapon")]
public bool HoldTrigger
{
@@ -39,7 +53,7 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(1, IsPropertySaveable.No, description: "How projectiles the weapon launches when fired once.")]
[Serialize(1, IsPropertySaveable.No, description: "How many projectiles the weapon launches when fired once.")]
public int ProjectileCount
{
get;
@@ -60,6 +74,23 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(0.0f, IsPropertySaveable.No, description: "The impulse applied to the physics body of the projectile (the higher the impulse, the faster the projectiles are launched). Sum of weapon + projectile.")]
public float LaunchImpulse
{
get;
set;
}
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Percentage of damage mitigation ignored when hitting armored body parts (deflecting limbs). Sum of weapon + projectile."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1f)]
public float Penetration { get; private set; }
[Serialize(1f, IsPropertySaveable.Yes, description: "Weapon's damage modifier")]
public float WeaponDamageModifier
{
get;
private set;
}
[Serialize(0f, IsPropertySaveable.Yes, description: "The time required for a charge-type turret to charge up before able to fire.")]
public float MaxChargeTime
{
@@ -67,6 +98,9 @@ namespace Barotrauma.Items.Components
private set;
}
private readonly IReadOnlySet<Identifier> suitableProjectiles;
private enum ChargingState
{
Inactive,
@@ -99,6 +133,11 @@ namespace Barotrauma.Items.Components
// TODO: should define this in xml if we have ranged weapons that don't require aim to use
item.RequireAimToUse = true;
characterUsable = true;
suitableProjectiles = element.GetAttributeIdentifierArray(nameof(suitableProjectiles), Array.Empty<Identifier>()).ToHashSet();
if (ReloadSkillRequirement > 0 && ReloadNoSkill <= reload)
{
DebugConsole.AddWarning($"Invalid XML at {item.Name}: ReloadNoSkill is lower or equal than it's reload skill, despite having ReloadSkillRequirement.");
}
InitProjSpecific(element);
}
@@ -167,7 +206,15 @@ namespace Barotrauma.Items.Components
if (currentChargeTime < MaxChargeTime) { return false; }
IsActive = true;
ReloadTimer = reload / (1 + character?.GetStatValue(StatTypes.RangedAttackSpeed) ?? 0f);
float baseReloadTime = reload;
float weaponSkill = character.GetSkillLevel("weapons");
if (ReloadSkillRequirement > 0 && ReloadNoSkill > reload && weaponSkill < ReloadSkillRequirement)
{
//Examples, assuming 40 weapon skill required: 1 - 40/40 = 0 ... 1 - 0/40 = 1 ... 1 - 20 / 40 = 0.5
float reloadFailure = MathHelper.Clamp(1 - (weaponSkill / ReloadSkillRequirement), 0, 1);
baseReloadTime = MathHelper.Lerp(reload, ReloadNoSkill, reloadFailure);
}
ReloadTimer = baseReloadTime / (1 + character?.GetStatValue(StatTypes.RangedAttackSpeed) ?? 0f);
currentChargeTime = 0f;
if (character != null)
@@ -218,9 +265,9 @@ namespace Barotrauma.Items.Components
{
lastProjectile?.Item.GetComponent<Rope>()?.Snap();
}
float damageMultiplier = 1f + item.GetQualityModifier(Quality.StatType.FirepowerMultiplier);
float damageMultiplier = (1f + item.GetQualityModifier(Quality.StatType.FirepowerMultiplier)) * WeaponDamageModifier;
projectile.Launcher = item;
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: ignoredBodies.ToList(), createNetworkEvent: false, damageMultiplier);
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: ignoredBodies.ToList(), createNetworkEvent: false, damageMultiplier, LaunchImpulse);
projectile.Item.GetComponent<Rope>()?.Attach(Item, projectile.Item);
if (i == 0)
{
@@ -244,39 +291,41 @@ namespace Barotrauma.Items.Components
public Projectile FindProjectile(bool triggerOnUseOnContainers = false)
{
var containedItems = item.OwnInventory?.AllItemsMod;
if (containedItems == null) { return null; }
foreach (Item item in containedItems)
foreach (ItemContainer container in item.GetComponents<ItemContainer>())
{
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 it in containedItems)
{
if (it == null) { continue; }
var containedSubItems = it.OwnInventory?.AllItemsMod;
if (containedSubItems == null) { continue; }
foreach (Item subItem in containedSubItems)
foreach (Item containedItem in container.Inventory.AllItemsMod)
{
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...)
if (triggerOnUseOnContainers && subItem.Condition > 0.0f)
if (containedItem == null) { continue; }
Projectile projectile = containedItem.GetComponent<Projectile>();
if (IsSuitableProjectile(projectile)) { return projectile; }
//projectile not found, see if the contained item contains projectiles
var containedSubItems = containedItem.OwnInventory?.AllItemsMod;
if (containedSubItems == null) { continue; }
foreach (Item subItem in containedSubItems)
{
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, 1.0f);
}
if (projectile != null) { return projectile; }
if (subItem == null) { continue; }
Projectile subProjectile = 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...)
if (triggerOnUseOnContainers && subItem.Condition > 0.0f)
{
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, 1.0f);
}
if (IsSuitableProjectile(subProjectile)) { return subProjectile; }
}
}
}
return null;
}
private bool IsSuitableProjectile(Projectile projectile)
{
if (projectile?.Item == null) { return false; }
if (!suitableProjectiles.Any()) { return true; }
return suitableProjectiles.Any(s => projectile.Item.Prefab.Identifier == s || projectile.Item.HasTag(s));
}
partial void LaunchProjSpecific();
}
class AbilityRangedWeapon : AbilityObject, IAbilityItem
@@ -636,11 +636,14 @@ namespace Barotrauma.Items.Components
float addedDetachTime = deltaTime * (1f + user.GetStatValue(StatTypes.RepairToolDeattachTimeMultiplier)) * (1f + item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier));
levelResource.DeattachTimer += addedDetachTime;
#if CLIENT
Character.Controlled?.UpdateHUDProgressBar(
this,
targetItem.WorldPosition,
levelResource.DeattachTimer / levelResource.DeattachDuration,
GUIStyle.Red, GUIStyle.Green, "progressbar.deattaching");
if (targetItem.Prefab.ShowHealthBar)
{
Character.Controlled?.UpdateHUDProgressBar(
this,
targetItem.WorldPosition,
levelResource.DeattachTimer / levelResource.DeattachDuration,
GUIStyle.Red, GUIStyle.Green, "progressbar.deattaching");
}
#endif
FixItemProjSpecific(user, deltaTime, targetItem, showProgressBar: false);
return true;
@@ -689,7 +692,7 @@ namespace Barotrauma.Items.Components
private float repairTimer;
private Gap previousGap;
private readonly float repairTimeOut = 5;
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
public override bool CrewAIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
{
if (!(objective.OperateTarget is Gap leak))
{
@@ -1,17 +1,26 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
class Throwable : Holdable
{
private float throwPos;
private bool throwing, throwDone;
enum ThrowState
{
None,
Initiated,
Throwing
}
private const float ThrowAngleStart = -MathHelper.PiOver2, ThrowAngleEnd = MathHelper.PiOver2;
private float throwAngle = ThrowAngleStart;
private bool midAir;
private ThrowState throwState;
//continuous collision detection is used while the item is moving faster than this
const float ContinuousCollisionThreshold = 5.0f;
@@ -27,7 +36,6 @@ namespace Barotrauma.Items.Components
public Throwable(Item item, ContentXElement element)
: base(item, element)
{
//throwForce = ToolBox.GetAttributeFloat(element, "throwforce", 1.0f);
if (aimPos == Vector2.Zero)
{
aimPos = new Vector2(0.6f, 0.1f);
@@ -36,22 +44,21 @@ namespace Barotrauma.Items.Components
public override bool Use(float deltaTime, Character character = null)
{
return characterUsable || character == null; //We do the actual throwing in Aim because Use might be used by chems
//actual throwing logic is handled in Update
return characterUsable || character == null;
}
public override bool SecondaryUse(float deltaTime, Character character = null)
{
if (!throwDone) return false; //This should only be triggered in update
throwDone = false;
return true;
//actual throwing logic is handled in Update - SecondaryUse only triggers when the item is thrown
return false;
}
public override void Drop(Character dropper)
{
base.Drop(dropper);
throwing = false;
throwPos = 0.0f;
throwState = ThrowState.None;
throwAngle = ThrowAngleStart;
}
public override void UpdateBroken(float deltaTime, Camera cam)
@@ -100,13 +107,22 @@ namespace Barotrauma.Items.Components
return;
}
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.CanAim;
if (throwState != ThrowState.Throwing)
{
if (picker.IsKeyDown(InputType.Aim))
{
if (picker.IsKeyDown(InputType.Shoot)) { throwState = ThrowState.Initiated; }
}
else if (throwState != ThrowState.Initiated)
{
throwAngle = ThrowAngleStart;
}
}
bool aim = picker.IsKeyDown(InputType.Aim) && picker.CanAim;
if (picker.IsDead || !picker.AllowInput)
{
throwing = false;
throwState = ThrowState.None;
aim = false;
}
@@ -124,25 +140,29 @@ namespace Barotrauma.Items.Components
item.Submarine = picker.Submarine;
if (!throwing)
if (throwState != ThrowState.Throwing)
{
if (aim)
if (aim || throwState == ThrowState.Initiated)
{
throwPos = MathUtils.WrapAnglePi(System.Math.Min(throwPos + deltaTime * 5.0f, MathHelper.PiOver2));
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, throwPos);
throwAngle = System.Math.Min(throwAngle + deltaTime * 8.0f, ThrowAngleEnd);
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, throwAngle);
if (throwAngle >= ThrowAngleEnd && throwState == ThrowState.Initiated)
{
throwState = ThrowState.Throwing;
}
}
else
{
throwPos = 0;
throwAngle = ThrowAngleStart;
ac.HoldItem(deltaTime, item, handlePos, holdPos, Vector2.Zero, aim: false, holdAngle);
}
}
else
{
throwPos = MathUtils.WrapAnglePi(throwPos - deltaTime * 15.0f);
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, throwPos);
throwAngle = MathUtils.WrapAnglePi(throwAngle - deltaTime * 15.0f);
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, throwAngle);
if (throwPos < 0)
if (throwAngle < 0)
{
Vector2 throwVector = Vector2.Normalize(picker.CursorWorldPosition - picker.WorldPosition);
//throw upwards if cursor is at the position of the character
@@ -180,8 +200,7 @@ namespace Barotrauma.Items.Components
Limb rightHand = ac.GetLimb(LimbType.RightHand);
item.body.AngularVelocity = rightHand.body.AngularVelocity;
throwPos = 0;
throwDone = true;
throwAngle = ThrowAngleStart;
IsActive = true;
if (GameMain.NetworkMember is { IsServer: true })
@@ -193,7 +212,7 @@ namespace Barotrauma.Items.Components
//Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, CurrentThrower, user: CurrentThrower);
}
throwing = false;
throwState = ThrowState.None;
}
}
}