Merge remote-tracking branch 'upstream/master' into develop
This commit is contained in:
@@ -32,11 +32,6 @@ namespace Barotrauma.Items.Components
|
||||
private LocalizedString prevMsg;
|
||||
private Dictionary<RelatedItem.RelationType, List<RelatedItem>> prevRequiredItems;
|
||||
|
||||
//the distance from the holding characters elbow to center of the physics body of the item
|
||||
protected Vector2 holdPos;
|
||||
|
||||
protected Vector2 aimPos;
|
||||
|
||||
private float swingState;
|
||||
|
||||
private Character prevEquipper;
|
||||
@@ -131,6 +126,9 @@ namespace Barotrauma.Items.Components
|
||||
get { return ConvertUnits.ToDisplayUnits(holdPos); }
|
||||
set { holdPos = ConvertUnits.ToSimUnits(value); }
|
||||
}
|
||||
//the distance from the holding characters elbow to center of the physics body of the item
|
||||
protected Vector2 holdPos;
|
||||
|
||||
|
||||
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position the character holds the item at when aiming (in pixels, as an offset from the character's shoulder)."+
|
||||
" Works similarly as HoldPos, except that the position is rotated according to the direction the player is aiming at. For example, a value of 10,-100 would make the character hold the item 100 pixels below the shoulder and 10 pixels forwards when aiming directly to the right.")]
|
||||
@@ -139,6 +137,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return ConvertUnits.ToDisplayUnits(aimPos); }
|
||||
set { aimPos = ConvertUnits.ToSimUnits(value); }
|
||||
}
|
||||
protected Vector2 aimPos;
|
||||
|
||||
protected float holdAngle;
|
||||
#if DEBUG
|
||||
@@ -258,12 +257,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
canBePicked = true;
|
||||
prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(RequiredItems);
|
||||
|
||||
if (attachable)
|
||||
{
|
||||
prevMsg = DisplayMsg;
|
||||
prevPickKey = PickKey;
|
||||
prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(requiredItems);
|
||||
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
@@ -320,7 +319,7 @@ namespace Barotrauma.Items.Components
|
||||
if (attachable)
|
||||
{
|
||||
prevMsg = DisplayMsg;
|
||||
prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(requiredItems);
|
||||
prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(RequiredItems);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,7 +648,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
DisplayMsg = prevMsg;
|
||||
PickKey = prevPickKey;
|
||||
requiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(prevRequiredItems);
|
||||
RequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(prevRequiredItems);
|
||||
|
||||
Attached = true;
|
||||
#if CLIENT
|
||||
@@ -667,7 +666,7 @@ namespace Barotrauma.Items.Components
|
||||
item.DrawDepthOffset = 0.0f;
|
||||
#endif
|
||||
//make the item pickable with the default pick key and with no specific tools/items when it's deattached
|
||||
requiredItems.Clear();
|
||||
RequiredItems.Clear();
|
||||
DisplayMsg = "";
|
||||
PickKey = InputType.Select;
|
||||
#if CLIENT
|
||||
@@ -916,15 +915,25 @@ namespace Barotrauma.Items.Components
|
||||
bool aim = picker.IsKeyDown(InputType.Aim) && aimPos != Vector2.Zero && picker.CanAim && !UsageDisabledByRangedWeapon(picker);
|
||||
if (aim)
|
||||
{
|
||||
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swingPos, aimPos + swingPos, aim, holdAngle, aimAngle);
|
||||
if (picker.AnimController.IsHoldingToRope && GetRope() is { Snapped: false } rope)
|
||||
{
|
||||
Vector2 targetPos = Submarine.GetRelativeSimPosition(picker, rope.Item);
|
||||
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, itemPos: aimPos, aim: true, holdAngle, aimAngle, targetPos: targetPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, itemPos: aimPos + swingPos, aim: true, holdAngle, aimAngle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swingPos, aimPos + swingPos, aim, holdAngle);
|
||||
var rope = GetRope();
|
||||
if (rope != null && rope.SnapWhenNotAimed && rope.Item.ParentInventory == null)
|
||||
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, itemPos: holdPos + swingPos, aim: false, holdAngle);
|
||||
if (GetRope() is { SnapWhenNotAimed: true } rope)
|
||||
{
|
||||
rope.Snap();
|
||||
if (rope.Item.ParentInventory == null)
|
||||
{
|
||||
rope.Snap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1054,15 +1063,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
var tempMsg = DisplayMsg;
|
||||
var tempRequiredItems = requiredItems;
|
||||
var tempRequiredItems = RequiredItems;
|
||||
|
||||
DisplayMsg = prevMsg;
|
||||
requiredItems = prevRequiredItems;
|
||||
RequiredItems = prevRequiredItems;
|
||||
|
||||
XElement saveElement = base.Save(parentElement);
|
||||
|
||||
DisplayMsg = tempMsg;
|
||||
requiredItems = tempRequiredItems;
|
||||
RequiredItems = tempRequiredItems;
|
||||
|
||||
return saveElement;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
holdable.Reattachable = false;
|
||||
if (requiredItems.Any())
|
||||
if (RequiredItems.Any())
|
||||
{
|
||||
holdable.PickingTime = float.MaxValue;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,9 @@ namespace Barotrauma.Items.Components
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
base.Equip(character);
|
||||
reloadTimer = Math.Min(reload, 1.0f);
|
||||
//force a wait of at least 1 second when equipping the weapon, so you can't "rapid-fire" by swapping between weapons
|
||||
const float forcedDelayOnEquip = 1.0f;
|
||||
reloadTimer = Math.Max(Math.Min(reload, forcedDelayOnEquip), reloadTimer);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -218,13 +220,12 @@ namespace Barotrauma.Items.Components
|
||||
AnimController ac = picker.AnimController;
|
||||
if (!hitting)
|
||||
{
|
||||
bool aim = item.RequireAimToUse && picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && picker.CanAim &&
|
||||
!UsageDisabledByRangedWeapon(picker);
|
||||
bool aim = item.RequireAimToUse && picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && picker.CanAim && !UsageDisabledByRangedWeapon(picker);
|
||||
if (aim)
|
||||
{
|
||||
UpdateSwingPos(deltaTime, out Vector2 swingPos);
|
||||
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 3f, MathHelper.PiOver4));
|
||||
ac.HoldItem(deltaTime, item, handlePos, aimPos + swingPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos + aimAngle, aimMelee: true);
|
||||
ac.HoldItem(deltaTime, item, handlePos, itemPos: aimPos + swingPos, aim: false, hitPos, holdAngle + hitPos + aimAngle, aimMelee: true);
|
||||
if (ac.InWater)
|
||||
{
|
||||
ac.LockFlipping();
|
||||
@@ -233,7 +234,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
hitPos = 0;
|
||||
ac.HoldItem(deltaTime, item, handlePos, holdPos, Vector2.Zero, aim: false, holdAngle);
|
||||
ac.HoldItem(deltaTime, item, handlePos, itemPos: holdPos, aim: false, holdAngle);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -242,11 +243,11 @@ namespace Barotrauma.Items.Components
|
||||
hitPos -= deltaTime * 15f;
|
||||
if (Swing)
|
||||
{
|
||||
ac.HoldItem(deltaTime, item, handlePos, SwingPos, Vector2.Zero, aim: false, hitPos, holdAngle);
|
||||
ac.HoldItem(deltaTime, item, handlePos, itemPos: SwingPos, aim: false, hitPos, holdAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
ac.HoldItem(deltaTime, item, handlePos, holdPos, Vector2.Zero, aim: false, holdAngle);
|
||||
ac.HoldItem(deltaTime, item, handlePos, itemPos: holdPos, aim: false, holdAngle);
|
||||
}
|
||||
if (hitPos < -MathHelper.Pi)
|
||||
{
|
||||
|
||||
@@ -82,9 +82,9 @@ namespace Barotrauma.Items.Components
|
||||
var abilityPickingTime = new AbilityItemPickingTime(PickingTime, item.Prefab);
|
||||
picker.CheckTalents(AbilityEffectType.OnItemPicked, abilityPickingTime);
|
||||
|
||||
if (requiredItems.ContainsKey(RelatedItem.RelationType.Equipped))
|
||||
if (RequiredItems.ContainsKey(RelatedItem.RelationType.Equipped))
|
||||
{
|
||||
foreach (RelatedItem ri in requiredItems[RelatedItem.RelationType.Equipped])
|
||||
foreach (RelatedItem ri in RequiredItems[RelatedItem.RelationType.Equipped])
|
||||
{
|
||||
foreach (var heldItem in picker.HeldItems)
|
||||
{
|
||||
|
||||
@@ -95,6 +95,20 @@ namespace Barotrauma.Items.Components
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: 1f, IsPropertySaveable.Yes, description: "Penalty multiplier to reload time when dual-wielding.")]
|
||||
public float DualWieldReloadTimePenaltyMultiplier
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(defaultValue: 0f, IsPropertySaveable.Yes, description: "Additive penalty to accuracy (spread angle) when dual-wielding.")]
|
||||
public float DualWieldAccuracyPenalty
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private readonly IReadOnlySet<Identifier> suitableProjectiles;
|
||||
|
||||
@@ -128,8 +142,11 @@ namespace Barotrauma.Items.Components
|
||||
: base(item, element)
|
||||
{
|
||||
item.IsShootable = true;
|
||||
// TODO: should define this in xml if we have ranged weapons that don't require aim to use
|
||||
item.RequireAimToUse = true;
|
||||
if (element.Parent is { } parent)
|
||||
{
|
||||
item.RequireAimToUse = parent.GetAttributeBool(nameof(item.RequireAimToUse), true);
|
||||
}
|
||||
|
||||
characterUsable = true;
|
||||
suitableProjectiles = element.GetAttributeIdentifierArray(nameof(suitableProjectiles), Array.Empty<Identifier>()).ToHashSet();
|
||||
if (ReloadSkillRequirement > 0 && ReloadNoSkill <= reload)
|
||||
@@ -140,7 +157,7 @@ namespace Barotrauma.Items.Components
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
partial void InitProjSpecific(ContentXElement rangedWeaponElement);
|
||||
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
@@ -194,9 +211,28 @@ namespace Barotrauma.Items.Components
|
||||
float degreeOfFailure = MathHelper.Clamp(1.0f - DegreeOfSuccess(user), 0.0f, 1.0f);
|
||||
degreeOfFailure *= degreeOfFailure;
|
||||
float spread = MathHelper.Lerp(Spread, UnskilledSpread, degreeOfFailure) / (1f + user.GetStatValue(StatTypes.RangedSpreadReduction));
|
||||
if (user.IsDualWieldingRangedWeapons())
|
||||
{
|
||||
spread += Math.Max(0f, ApplyDualWieldPenaltyReduction(user, DualWieldAccuracyPenalty, neutralValue: 0f));
|
||||
}
|
||||
return MathHelper.ToRadians(spread);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lerps between the original penalty and a neutral value, which should be 1 for multipliers and 0 for additive penalties.
|
||||
/// </summary>
|
||||
/// <param name="character">The character to get stat values from</param>
|
||||
/// <param name="originalPenalty">The original penalty value</param>
|
||||
/// <param name="neutralValue">Neutral value to lerp towards. Should be 1 for multipliers and 0 for additives.</param>
|
||||
/// <returns></returns>
|
||||
private static float ApplyDualWieldPenaltyReduction(Character character, float originalPenalty, float neutralValue)
|
||||
{
|
||||
float statAdjustmentPrc = character.GetStatValue(StatTypes.DualWieldingPenaltyReduction);
|
||||
statAdjustmentPrc = MathHelper.Clamp(statAdjustmentPrc, 0f, 1f);
|
||||
float reducedPenaltyMultiplier = MathHelper.Lerp(originalPenalty, neutralValue, statAdjustmentPrc);
|
||||
return reducedPenaltyMultiplier;
|
||||
}
|
||||
|
||||
private readonly List<Body> ignoredBodies = new List<Body>();
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
@@ -208,22 +244,27 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
float baseReloadTime = reload;
|
||||
float weaponSkill = character.GetSkillLevel("weapons");
|
||||
if (ReloadSkillRequirement > 0 && ReloadNoSkill > reload && weaponSkill < ReloadSkillRequirement)
|
||||
|
||||
bool applyReloadFailure = ReloadSkillRequirement > 0 && ReloadNoSkill > reload && weaponSkill < ReloadSkillRequirement;
|
||||
if (applyReloadFailure)
|
||||
{
|
||||
//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);
|
||||
}
|
||||
|
||||
if (character.IsDualWieldingRangedWeapons())
|
||||
{
|
||||
baseReloadTime *= Math.Max(1f, ApplyDualWieldPenaltyReduction(character, DualWieldReloadTimePenaltyMultiplier, neutralValue: 1f));
|
||||
}
|
||||
|
||||
ReloadTimer = baseReloadTime / (1 + character?.GetStatValue(StatTypes.RangedAttackSpeed) ?? 0f);
|
||||
ReloadTimer /= 1f + item.GetQualityModifier(Quality.StatType.FiringRateMultiplier);
|
||||
|
||||
currentChargeTime = 0f;
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
var abilityRangedWeapon = new AbilityRangedWeapon(item);
|
||||
character.CheckTalents(AbilityEffectType.OnUseRangedWeapon, abilityRangedWeapon);
|
||||
}
|
||||
var abilityRangedWeapon = new AbilityRangedWeapon(item);
|
||||
character.CheckTalents(AbilityEffectType.OnUseRangedWeapon, abilityRangedWeapon);
|
||||
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
|
||||
@@ -90,12 +90,11 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item repair things through holes in walls.")]
|
||||
public bool RepairThroughHoles { get; set; }
|
||||
|
||||
|
||||
[Serialize(100.0f, IsPropertySaveable.No, description: "How far two walls need to not be considered overlapping and to stop the ray.")]
|
||||
public float MaxOverlappingWallDist
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public float MaxOverlappingWallDist { get; set; }
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "How fast the tool detaches level resources (e.g. minerals). Acts as a multiplier on the speed: with a value of 2, detaching an item whose DeattachDuration is set to 30 seconds would take 15 seconds.")]
|
||||
public float DeattachSpeed { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Can the item hit doors.")]
|
||||
public bool HitItems { get; set; }
|
||||
@@ -171,7 +170,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
item.IsShootable = true;
|
||||
item.RequireAimToUse = element.Parent.GetAttributeBool("requireaimtouse", true);
|
||||
item.RequireAimToUse = element.Parent.GetAttributeBool(nameof(item.RequireAimToUse), true);
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
@@ -321,7 +320,7 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<FireSource> fireSourcesInRange = new List<FireSource>();
|
||||
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies)
|
||||
{
|
||||
var collisionCategories = Physics.CollisionWall | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair;
|
||||
var collisionCategories = Physics.CollisionWall | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepairableWall;
|
||||
if (!IgnoreCharacters)
|
||||
{
|
||||
collisionCategories |= Physics.CollisionCharacter;
|
||||
@@ -414,7 +413,7 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
pickedPosition = rayStart + (rayEnd - rayStart) * thisBodyFraction;
|
||||
if (FixBody(user, deltaTime, degreeOfSuccess, body))
|
||||
if (FixBody(user, pickedPosition, deltaTime, degreeOfSuccess, body))
|
||||
{
|
||||
lastPickedFraction = thisBodyFraction;
|
||||
if (bodyType != null) { lastHitType = bodyType; }
|
||||
@@ -452,7 +451,7 @@ namespace Barotrauma.Items.Components
|
||||
},
|
||||
allowInsideFixture: true);
|
||||
pickedPosition = Submarine.LastPickedPosition;
|
||||
FixBody(user, deltaTime, degreeOfSuccess, pickedBody);
|
||||
FixBody(user, pickedPosition, deltaTime, degreeOfSuccess, pickedBody);
|
||||
lastPickedFraction = Submarine.LastPickedFraction;
|
||||
}
|
||||
|
||||
@@ -543,7 +542,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private bool FixBody(Character user, float deltaTime, float degreeOfSuccess, Body targetBody)
|
||||
private bool FixBody(Character user, Vector2 hitPosition, float deltaTime, float degreeOfSuccess, Body targetBody)
|
||||
{
|
||||
if (targetBody?.UserData == null) { return false; }
|
||||
|
||||
@@ -600,7 +599,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (Level.Loaded?.ExtraWalls.Find(w => w.Body == cell.Body) is DestructibleLevelWall levelWall)
|
||||
{
|
||||
levelWall.AddDamage(-LevelWallFixAmount * deltaTime, item.WorldPosition);
|
||||
levelWall.AddDamage(-LevelWallFixAmount * deltaTime, ConvertUnits.ToDisplayUnits(hitPosition));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -661,10 +660,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
var levelResource = targetItem.GetComponent<LevelResource>();
|
||||
if (levelResource != null && levelResource.Attached &&
|
||||
levelResource.requiredItems.Any() &&
|
||||
levelResource.RequiredItems.Any() &&
|
||||
levelResource.HasRequiredItems(user, addMessage: false))
|
||||
{
|
||||
float addedDetachTime = deltaTime * (1f + user.GetStatValue(StatTypes.RepairToolDeattachTimeMultiplier)) * (1f + item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier));
|
||||
float addedDetachTime = deltaTime *
|
||||
DeattachSpeed *
|
||||
(1f + user.GetStatValue(StatTypes.RepairToolDeattachTimeMultiplier)) *
|
||||
(1f + item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier));
|
||||
levelResource.DeattachTimer += addedDetachTime;
|
||||
#if CLIENT
|
||||
if (targetItem.Prefab.ShowHealthBar && Character.Controlled != null &&
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace Barotrauma.Items.Components
|
||||
if (aim || throwState == ThrowState.Initiated)
|
||||
{
|
||||
throwAngle = System.Math.Min(throwAngle + deltaTime * 8.0f, ThrowAngleEnd);
|
||||
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, throwAngle);
|
||||
ac.HoldItem(deltaTime, item, handlePos, itemPos: aimPos, aim: false, throwAngle);
|
||||
if (throwAngle >= ThrowAngleEnd && throwState == ThrowState.Initiated)
|
||||
{
|
||||
throwState = ThrowState.Throwing;
|
||||
@@ -158,13 +158,13 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
throwAngle = ThrowAngleStart;
|
||||
ac.HoldItem(deltaTime, item, handlePos, holdPos, Vector2.Zero, aim: false, holdAngle);
|
||||
ac.HoldItem(deltaTime, item, handlePos, itemPos: aimPos, aim: false, holdAngle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAngle = MathUtils.WrapAnglePi(throwAngle - deltaTime * 15.0f);
|
||||
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, throwAngle);
|
||||
ac.HoldItem(deltaTime, item, handlePos, itemPos: aimPos, aim: false, throwAngle);
|
||||
|
||||
if (throwAngle < 0)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user