0.1500.3.0 (🗿 edition)
This commit is contained in:
@@ -161,8 +161,8 @@ namespace Barotrauma
|
||||
for (int i = 0; i < container.Inventory.Capacity; i++)
|
||||
{
|
||||
if (container.Inventory.GetItemAt(i) != null) { continue; }
|
||||
if (MapEntityPrefab.List.GetRandom(e => e is ItemPrefab i && container.CanBeContained(i) &&
|
||||
Config.ForbiddenAmmunition.None(id => id.Equals(i.Identifier, StringComparison.OrdinalIgnoreCase)), Rand.RandSync.Server) is ItemPrefab ammoPrefab)
|
||||
if (MapEntityPrefab.List.GetRandom(e => e is ItemPrefab ip && container.CanBeContained(ip, i) &&
|
||||
Config.ForbiddenAmmunition.None(id => id.Equals(ip.Identifier, StringComparison.OrdinalIgnoreCase)), Rand.RandSync.Server) is ItemPrefab ammoPrefab)
|
||||
{
|
||||
Item ammo = new Item(ammoPrefab, container.Item.WorldPosition, Wreck);
|
||||
if (!container.Inventory.TryPutItem(ammo, i, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: false))
|
||||
|
||||
@@ -1430,6 +1430,7 @@ namespace Barotrauma
|
||||
|
||||
//throwing conscious/moving characters around takes more force -> double the flow force
|
||||
if (character.CanMove) { flowForce *= 2.0f; }
|
||||
flowForce *= 1 - Math.Clamp(character.GetStatValue(StatTypes.FlowResistance), 0f, 1f);
|
||||
|
||||
float flowForceMagnitude = flowForce.Length();
|
||||
float limbMultipier = limbs.Count(l => l.inWater) / (float)limbs.Length;
|
||||
|
||||
@@ -251,6 +251,8 @@ namespace Barotrauma
|
||||
private readonly List<Attacker> lastAttackers = new List<Attacker>();
|
||||
public IEnumerable<Attacker> LastAttackers => lastAttackers;
|
||||
public Character LastAttacker => lastAttackers.LastOrDefault()?.Character;
|
||||
public Character LastOrderedCharacter { get; private set; }
|
||||
public Character SecondLastOrderedCharacter { get; private set; }
|
||||
|
||||
public Entity LastDamageSource;
|
||||
|
||||
@@ -2720,7 +2722,7 @@ namespace Barotrauma
|
||||
|
||||
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
|
||||
bool allowRagdoll = GameMain.NetworkMember?.ServerSettings?.AllowRagdollButton ?? true;
|
||||
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 5.0f * 5.0f;
|
||||
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 2.5f * 2.5f;
|
||||
bool wasRagdolled = false;
|
||||
bool selfRagdolled = false;
|
||||
|
||||
@@ -3131,6 +3133,12 @@ namespace Barotrauma
|
||||
{
|
||||
var abilityOrderedCharacter = new AbilityCharacter(this);
|
||||
orderGiver.CheckTalents(AbilityEffectType.OnGiveOrder, abilityOrderedCharacter);
|
||||
|
||||
if (orderGiver.LastOrderedCharacter != this)
|
||||
{
|
||||
orderGiver.SecondLastOrderedCharacter = orderGiver.LastOrderedCharacter;
|
||||
orderGiver.LastOrderedCharacter = this;
|
||||
}
|
||||
}
|
||||
|
||||
if (AIController is HumanAIController humanAI)
|
||||
@@ -3406,6 +3414,13 @@ namespace Barotrauma
|
||||
AddDamage(worldPosition, attackAfflictions, attack.Stun, playSound, attackImpulse, out limbHit, attacker, attack.DamageMultiplier * attackData.DamageMultiplier) :
|
||||
DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration);
|
||||
|
||||
if (attacker != null)
|
||||
{
|
||||
var abilityAttackResult = new AbilityAttackResult(attackResult);
|
||||
attacker.CheckTalents(AbilityEffectType.OnAttackResult, abilityAttackResult);
|
||||
CheckTalents(AbilityEffectType.OnAttackedResult, abilityAttackResult);
|
||||
}
|
||||
|
||||
if (limbHit == null) { return new AttackResult(); }
|
||||
Vector2 forceWorld = attack.TargetImpulseWorld + attack.TargetForceWorld;
|
||||
if (attacker != null)
|
||||
@@ -3623,11 +3638,6 @@ namespace Barotrauma
|
||||
ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
|
||||
hitLimb.ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
|
||||
}
|
||||
if (attacker != null)
|
||||
{
|
||||
var abilityAttackResult = new AbilityAttackResult(attackResult);
|
||||
attacker.CheckTalents(AbilityEffectType.OnAttackResult, abilityAttackResult);
|
||||
}
|
||||
|
||||
return attackResult;
|
||||
}
|
||||
|
||||
@@ -979,6 +979,8 @@ namespace Barotrauma
|
||||
increase *= SkillSettings.Current.AssistantSkillIncreaseMultiplier;
|
||||
}
|
||||
|
||||
increase *= 1f + Character.GetStatValue(StatTypes.SkillGainSpeed);
|
||||
|
||||
float prevLevel = Job.GetSkillLevel(skillIdentifier);
|
||||
Job.IncreaseSkillLevel(skillIdentifier, increase, Character.HasAbilityFlag(AbilityFlags.GainSkillPastMaximum));
|
||||
|
||||
@@ -1527,6 +1529,17 @@ namespace Barotrauma
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
public float GetSavedStatValue(StatTypes statType, string statIdentifier)
|
||||
{
|
||||
if (savedStatValues.TryGetValue(statType, out var statValues))
|
||||
{
|
||||
return statValues.Where(s => s.StatIdentifier.Equals(statIdentifier, StringComparison.OrdinalIgnoreCase)).Sum(v => v.StatValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath, bool removeAfterRound = false, float maxValue = float.MaxValue)
|
||||
{
|
||||
|
||||
@@ -971,7 +971,7 @@ namespace Barotrauma
|
||||
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
|
||||
/// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param>
|
||||
/// <param name="randomization">Amount of randomization to apply to the values (0 = the values are accurate, 1 = the values are completely random)</param>
|
||||
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, float randomization = 0.0f)
|
||||
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, bool ignoreHiddenAfflictions = false, float randomization = 0.0f)
|
||||
{
|
||||
//key = item identifier
|
||||
//float = suitability
|
||||
@@ -980,6 +980,7 @@ namespace Barotrauma
|
||||
foreach (Affliction affliction in getAfflictions(limb))
|
||||
{
|
||||
if (affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
if (ignoreHiddenAfflictions && affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
|
||||
foreach (KeyValuePair<string, float> treatment in affliction.Prefab.TreatmentSuitability)
|
||||
{
|
||||
if (!treatmentSuitability.ContainsKey(treatment.Key))
|
||||
|
||||
+4
-4
@@ -7,12 +7,12 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
private enum WeaponType
|
||||
{
|
||||
Any = 0,
|
||||
Melee = 1,
|
||||
Any = 0,
|
||||
Melee = 1,
|
||||
Ranged = 2
|
||||
};
|
||||
|
||||
private WeaponType weapontype;
|
||||
private readonly WeaponType weapontype;
|
||||
public AbilityConditionIsAiming(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
switch (conditionElement.GetAttributeString("weapontype", ""))
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma.Abilities
|
||||
break;
|
||||
default:
|
||||
aimingCorrectItem |= animController.IsAiming || animController.IsAimingMelee;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -6,12 +6,12 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionItem : AbilityConditionData
|
||||
{
|
||||
private readonly string identifier;
|
||||
private readonly string[] identifiers;
|
||||
private readonly string[] tags;
|
||||
|
||||
public AbilityConditionItem(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
identifier = conditionElement.GetAttributeString("identifier", string.Empty).ToLowerInvariant();
|
||||
identifiers = conditionElement.GetAttributeStringArray("identifiers", Array.Empty<string>(), convertToLowerInvariant: true);
|
||||
tags = conditionElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
|
||||
}
|
||||
|
||||
@@ -29,15 +29,15 @@ namespace Barotrauma.Abilities
|
||||
|
||||
if (itemPrefab != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(identifier))
|
||||
if (identifiers.Any())
|
||||
{
|
||||
if (itemPrefab.Identifier != identifier)
|
||||
if (!identifiers.Any(t => itemPrefab.Identifier == t))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return tags.Any(t => itemPrefab.Tags.Any(p => t == p));
|
||||
return !tags.Any() || tags.Any(t => itemPrefab.Tags.Any(p => t == p));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionHasVelocity : AbilityConditionDataless
|
||||
{
|
||||
private readonly float velocity;
|
||||
|
||||
public AbilityConditionHasVelocity(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
velocity = conditionElement.GetAttributeFloat("velocity", 0f);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
return character.AnimController.Collider.LinearVelocity.LengthSquared() > velocity * velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
if (character.Submarine == null || character.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (!character.IsInFriendlySub) { return false; }
|
||||
float currentFloodPercentage = character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
|
||||
return currentFloodPercentage / 100 > floodPercentage;
|
||||
}
|
||||
|
||||
+7
-1
@@ -10,6 +10,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected readonly List<StatusEffect> statusEffects;
|
||||
|
||||
private readonly bool nearbyCharactersAppliesToSelf;
|
||||
private readonly bool applyToSelected;
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
@@ -18,6 +19,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
applyToSelected = abilityElement.GetAttributeBool("applytoselected", false);
|
||||
nearbyCharactersAppliesToSelf = abilityElement.GetAttributeBool("nearbycharactersappliestoself", true);
|
||||
}
|
||||
|
||||
protected void ApplyEffectSpecific(Character targetCharacter)
|
||||
@@ -26,7 +28,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
||||
{
|
||||
// currently used this to spawn items on the targeted character
|
||||
// currently used to spawn items on the targeted character
|
||||
statusEffect.SetUser(targetCharacter);
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targetCharacter);
|
||||
}
|
||||
@@ -34,6 +36,10 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
targets.Clear();
|
||||
targets.AddRange(statusEffect.GetNearbyTargets(targetCharacter.WorldPosition, targets));
|
||||
if (!nearbyCharactersAppliesToSelf)
|
||||
{
|
||||
targets.RemoveAll(c => c == Character);
|
||||
}
|
||||
statusEffect.SetUser(Character);
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targets);
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityApplyStatusEffectsToLastOrderedCharacter : CharacterAbilityApplyStatusEffects
|
||||
{
|
||||
public CharacterAbilityApplyStatusEffectsToLastOrderedCharacter(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
if (IsViableTarget(Character.LastOrderedCharacter))
|
||||
{
|
||||
ApplyEffectSpecific(Character.LastOrderedCharacter);
|
||||
}
|
||||
if (Character.HasAbilityFlag(AbilityFlags.AllowSecondOrderedTarget) && IsViableTarget(Character.SecondLastOrderedCharacter))
|
||||
{
|
||||
ApplyEffectSpecific(Character.SecondLastOrderedCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsViableTarget(Character targetCharacter)
|
||||
{
|
||||
if (targetCharacter == null || targetCharacter.Removed) { return false; }
|
||||
if (targetCharacter == Character) { return false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -27,8 +27,7 @@ namespace Barotrauma.Abilities
|
||||
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
|
||||
removeOnDeath = abilityElement.GetAttributeBool("removeondeath", true);
|
||||
removeAfterRound = abilityElement.GetAttributeBool("removeafterround", false);
|
||||
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", false);
|
||||
//maximumValue = abilityElement.GetAttributeFloat("maximumvalue", float.MaxValue);
|
||||
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", characterAbilityGroup.AbilityEffectType == AbilityEffectType.None);
|
||||
}
|
||||
|
||||
public override void InitializeAbility(bool addingFirstTime)
|
||||
|
||||
+24
-4
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
@@ -7,13 +8,22 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
|
||||
private string skillIdentifier;
|
||||
private float skillIncrease;
|
||||
private readonly string skillIdentifier;
|
||||
private readonly float skillIncrease;
|
||||
|
||||
public CharacterAbilityIncreaseSkill(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
skillIdentifier = abilityElement.GetAttributeString("skillidentifier", "").ToLowerInvariant();
|
||||
skillIncrease = abilityElement.GetAttributeFloat("skillincrease", 0f);
|
||||
|
||||
if (string.IsNullOrEmpty(skillIdentifier))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill identifier not defined in CharacterAbilityIncreaseSkill.");
|
||||
}
|
||||
if (MathUtils.NearlyEqual(skillIncrease, 0))
|
||||
{
|
||||
DebugConsole.AddWarning($"Possible error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill increase set to 0.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
@@ -35,7 +45,17 @@ namespace Barotrauma.Abilities
|
||||
|
||||
private void ApplyEffectSpecific(Character character)
|
||||
{
|
||||
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
|
||||
if (skillIdentifier.Equals("random"))
|
||||
{
|
||||
var skill = character.Info?.Job?.Skills?.GetRandom();
|
||||
if (skill == null) { return; }
|
||||
character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityModifyStatToFlooding : CharacterAbility
|
||||
{
|
||||
private readonly StatTypes statType;
|
||||
private readonly float maxValue;
|
||||
private float lastValue = 0f;
|
||||
|
||||
public CharacterAbilityModifyStatToFlooding(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
||||
maxValue = abilityElement.GetAttributeFloat("maxvalue", 0f);
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
Character.ChangeStat(statType, -lastValue);
|
||||
|
||||
if (conditionsMatched && Character.IsInFriendlySub)
|
||||
{
|
||||
float currentFloodPercentage = Character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
|
||||
lastValue = currentFloodPercentage / 100f * maxValue;
|
||||
Character.ChangeStat(statType, lastValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastValue = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -4,8 +4,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityModifyValue : CharacterAbility
|
||||
{
|
||||
private float addedValue;
|
||||
private float multiplyValue;
|
||||
private readonly float addedValue;
|
||||
private readonly float multiplyValue;
|
||||
|
||||
public CharacterAbilityModifyValue(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
|
||||
+8
-2
@@ -6,12 +6,14 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityByTheBook : CharacterAbility
|
||||
{
|
||||
private int moneyAmount;
|
||||
private int max;
|
||||
private readonly int moneyAmount;
|
||||
private readonly int experienceAmount;
|
||||
private readonly int max;
|
||||
|
||||
public CharacterAbilityByTheBook(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
moneyAmount = abilityElement.GetAttributeInt("moneyamount", 0);
|
||||
experienceAmount = abilityElement.GetAttributeInt("experienceamount", 0);
|
||||
max = abilityElement.GetAttributeInt("max", 0);
|
||||
}
|
||||
|
||||
@@ -28,6 +30,10 @@ namespace Barotrauma.Abilities
|
||||
if (!enemyCharacter.LockHands) { continue; }
|
||||
if (timesGiven > max) { continue; }
|
||||
Character.GiveMoney(moneyAmount);
|
||||
foreach (Character character in Character.GetFriendlyCrew(Character))
|
||||
{
|
||||
character.Info?.GiveExperience(experienceAmount);
|
||||
}
|
||||
timesGiven++;
|
||||
}
|
||||
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityEnigmaMachine : CharacterAbility
|
||||
{
|
||||
private readonly float addedValue;
|
||||
private readonly float multiplyValue;
|
||||
private readonly string[] tags;
|
||||
private readonly int maxMultiplyCount;
|
||||
|
||||
public CharacterAbilityEnigmaMachine(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
|
||||
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
|
||||
tags = abilityElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
|
||||
maxMultiplyCount = abilityElement.GetAttributeInt("maxmultiplycount", int.MaxValue);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if (abilityObject is IAbilityValue abilityValue)
|
||||
{
|
||||
int multiplyCount = 0;
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Prefab.Tags.Any(t => tags.Contains(t)))
|
||||
{
|
||||
multiplyCount++;
|
||||
if (multiplyCount == maxMultiplyCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
abilityValue.Value += addedValue * multiplyCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityIndustrialRevolution : CharacterAbility
|
||||
{
|
||||
float addedFabricationSpeed;
|
||||
|
||||
public CharacterAbilityIndustrialRevolution(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
addedFabricationSpeed = abilityElement.GetAttributeFloat("addedfabricationspeed", 0f);
|
||||
}
|
||||
|
||||
public override void UpdateCharacterAbility(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
if (conditionsMatched)
|
||||
{
|
||||
// not necessarily the cleanest or performant way, but at least this shouldn't break anything.
|
||||
// must be done every frame in order to work.
|
||||
if (Character.SelectedConstruction?.GetComponent<Fabricator>() is Fabricator fabricator && fabricator.IsActive)
|
||||
{
|
||||
fabricator.FabricationSpeedMultiplier += addedFabricationSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityTaskmaster : CharacterAbility
|
||||
{
|
||||
private readonly List<StatusEffect> statusEffects;
|
||||
private readonly List<StatusEffect> statusEffectsRemove;
|
||||
|
||||
private Character lastCharacter;
|
||||
|
||||
public CharacterAbilityTaskmaster(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
statusEffectsRemove = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffectsremove"));
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if ((abilityObject as IAbilityCharacter)?.Character is Character targetCharacter)
|
||||
{
|
||||
if (targetCharacter == Character) { return; }
|
||||
|
||||
foreach (var statusEffect in statusEffectsRemove)
|
||||
{
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, lastCharacter);
|
||||
}
|
||||
|
||||
foreach (var statusEffect in statusEffects)
|
||||
{
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, targetCharacter);
|
||||
}
|
||||
|
||||
lastCharacter = targetCharacter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -14,6 +14,8 @@ namespace Barotrauma.Abilities
|
||||
// currently only used to turn off simulation if random conditions are in use
|
||||
public bool IsActive { get; private set; } = true;
|
||||
|
||||
public readonly AbilityEffectType AbilityEffectType;
|
||||
|
||||
protected int maxTriggerCount { get; }
|
||||
protected int timesTriggered = 0;
|
||||
|
||||
@@ -24,8 +26,9 @@ namespace Barotrauma.Abilities
|
||||
// separate dictionaries for each type of characterability?
|
||||
protected readonly List<CharacterAbility> characterAbilities = new List<CharacterAbility>();
|
||||
|
||||
public CharacterAbilityGroup(CharacterTalent characterTalent, XElement abilityElementGroup)
|
||||
public CharacterAbilityGroup(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup)
|
||||
{
|
||||
AbilityEffectType = abilityEffectType;
|
||||
CharacterTalent = characterTalent;
|
||||
Character = CharacterTalent.Character;
|
||||
maxTriggerCount = abilityElementGroup.GetAttributeInt("maxtriggercount", int.MaxValue);
|
||||
@@ -168,8 +171,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
public static StatTypes ParseStatType(string statTypeString, string debugIdentifier)
|
||||
{
|
||||
StatTypes statType;
|
||||
if (!Enum.TryParse(statTypeString, true, out statType))
|
||||
if (!Enum.TryParse(statTypeString, true, out StatTypes statType))
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,7 +8,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityGroupEffect : CharacterAbilityGroup
|
||||
{
|
||||
public CharacterAbilityGroupEffect(CharacterTalent characterTalent, XElement abilityElementGroup) : base(characterTalent, abilityElementGroup) { }
|
||||
public CharacterAbilityGroupEffect(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup) :
|
||||
base(abilityEffectType, characterTalent, abilityElementGroup) { }
|
||||
|
||||
public void CheckAbilityGroup(AbilityObject abilityObject)
|
||||
{
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@ namespace Barotrauma.Abilities
|
||||
private float effectDelayTimer;
|
||||
|
||||
|
||||
public CharacterAbilityGroupInterval(CharacterTalent characterTalent, XElement abilityElementGroup) : base(characterTalent, abilityElementGroup)
|
||||
public CharacterAbilityGroupInterval(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup) :
|
||||
base(abilityEffectType, characterTalent, abilityElementGroup)
|
||||
{
|
||||
// too many overlapping intervals could cause hitching? maybe randomize a little
|
||||
interval = abilityElementGroup.GetAttributeFloat("interval", 0f);
|
||||
|
||||
@@ -85,14 +85,13 @@ namespace Barotrauma
|
||||
// XML logic
|
||||
private void LoadAbilityGroupInterval(XElement abilityGroup)
|
||||
{
|
||||
string name = abilityGroup.Name.ToString().ToLowerInvariant();
|
||||
characterAbilityGroupIntervals.Add(new CharacterAbilityGroupInterval(this, abilityGroup));
|
||||
characterAbilityGroupIntervals.Add(new CharacterAbilityGroupInterval(AbilityEffectType.Undefined, this, abilityGroup));
|
||||
}
|
||||
|
||||
private void LoadAbilityGroupEffect(XElement abilityGroup)
|
||||
{
|
||||
AbilityEffectType abilityEffectType = ParseAbilityEffectType(this, abilityGroup.GetAttributeString("abilityeffecttype", "none"));
|
||||
AddAbilityGroupEffect(new CharacterAbilityGroupEffect(this, abilityGroup), abilityEffectType);
|
||||
AddAbilityGroupEffect(new CharacterAbilityGroupEffect(abilityEffectType, this, abilityGroup), abilityEffectType);
|
||||
}
|
||||
|
||||
public void AddAbilityGroupEffect(CharacterAbilityGroupEffect characterAbilityGroup, AbilityEffectType abilityEffectType = AbilityEffectType.None)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -8,13 +7,19 @@ namespace Barotrauma
|
||||
{
|
||||
class TalentTree
|
||||
{
|
||||
public enum TalentTreeStageState
|
||||
{
|
||||
Invalid,
|
||||
Locked,
|
||||
Unlocked,
|
||||
Available,
|
||||
Highlighted
|
||||
}
|
||||
|
||||
public static readonly Dictionary<string, TalentTree> JobTalentTrees = new Dictionary<string, TalentTree>();
|
||||
|
||||
public readonly List<TalentSubTree> TalentSubTrees = new List<TalentSubTree>();
|
||||
|
||||
private static HashSet<string> subtreeTalents = new HashSet<string>();
|
||||
|
||||
private const string PlaceholderTalent = "placeholder";
|
||||
public XElement ConfigElement
|
||||
{
|
||||
get;
|
||||
@@ -35,14 +40,13 @@ namespace Barotrauma
|
||||
|
||||
foreach (XElement subTreeElement in element.GetChildElements("subtree"))
|
||||
{
|
||||
TalentSubTrees.Add(new TalentSubTree(subTreeElement));
|
||||
TalentSubTrees.Add(new TalentSubTree(subTreeElement));
|
||||
}
|
||||
|
||||
// talents found and unlocked using the identifier wihin the talent tree, so no duplicates may occur
|
||||
HashSet<string> duplicateSet = new HashSet<string>();
|
||||
foreach (string talent in TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier))))
|
||||
{
|
||||
if (talent == PlaceholderTalent) { continue; }
|
||||
TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c => c.Identifier.Equals(talent, StringComparison.OrdinalIgnoreCase));
|
||||
if (talentPrefab == null)
|
||||
{
|
||||
@@ -97,7 +101,7 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DebugConsole.ThrowError($"Invalid XML root element: '{rootElement.Name.ToString()}' in {file.Path}");
|
||||
DebugConsole.ThrowError($"Invalid XML root element: '{rootElement.Name}' in {file.Path}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -117,10 +121,63 @@ namespace Barotrauma
|
||||
return IsViableTalentForCharacter(character, talentIdentifier, character?.Info?.UnlockedTalents ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
// i hate this function - markus
|
||||
public static TalentTreeStageState GetTalentOptionStageState(Character character, string subTreeIdentifier, int index, List<string> selectedTalents)
|
||||
{
|
||||
if (character?.Info?.Job.Prefab is null) { return TalentTreeStageState.Invalid; }
|
||||
|
||||
if (!JobTalentTrees.TryGetValue(character.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return TalentTreeStageState.Invalid; }
|
||||
|
||||
TalentSubTree subTree = talentTree.TalentSubTrees.FirstOrDefault(tst => tst.Identifier == subTreeIdentifier);
|
||||
|
||||
if (subTree == null) { return TalentTreeStageState.Invalid; }
|
||||
|
||||
TalentOption targetTalentOption = subTree.TalentOptionStages[index];
|
||||
|
||||
if (targetTalentOption.Talents.Any(t => character.HasTalent(t.Identifier)))
|
||||
{
|
||||
return TalentTreeStageState.Unlocked;
|
||||
}
|
||||
|
||||
if (targetTalentOption.Talents.Any(t => selectedTalents.Contains(t.Identifier)))
|
||||
{
|
||||
return TalentTreeStageState.Highlighted;
|
||||
}
|
||||
|
||||
bool hasTalentInLastTier = true;
|
||||
bool isLastTalentPurchased = true;
|
||||
|
||||
int lastindex = index - 1;
|
||||
if (lastindex >= 0)
|
||||
{
|
||||
TalentOption lastLatentOption = subTree.TalentOptionStages[lastindex];
|
||||
hasTalentInLastTier = lastLatentOption.Talents.Any(HasTalent);
|
||||
isLastTalentPurchased = lastLatentOption.Talents.Any(t => character.HasTalent(t.Identifier));
|
||||
}
|
||||
|
||||
if (!hasTalentInLastTier)
|
||||
{
|
||||
return TalentTreeStageState.Locked;
|
||||
}
|
||||
|
||||
bool hasPointsForNewTalent = character.Info.GetTotalTalentPoints() - selectedTalents.Count > 0;
|
||||
|
||||
if (hasPointsForNewTalent)
|
||||
{
|
||||
return isLastTalentPurchased ? TalentTreeStageState.Highlighted : TalentTreeStageState.Available;
|
||||
}
|
||||
|
||||
return TalentTreeStageState.Locked;
|
||||
|
||||
bool HasTalent(TalentPrefab t)
|
||||
{
|
||||
return selectedTalents.Contains(t.Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static bool IsViableTalentForCharacter(Character character, string talentIdentifier, IEnumerable<string> selectedTalents)
|
||||
{
|
||||
if (talentIdentifier == PlaceholderTalent) { return false; }
|
||||
if (character?.Info?.Job.Prefab == null) { return false; }
|
||||
if (character.Info.GetTotalTalentPoints() - selectedTalents.Count() <= 0) { return false; }
|
||||
|
||||
@@ -173,12 +230,16 @@ namespace Barotrauma
|
||||
{
|
||||
public string Identifier { get; }
|
||||
|
||||
public string DisplayName { get; }
|
||||
|
||||
public readonly List<TalentOption> TalentOptionStages = new List<TalentOption>();
|
||||
|
||||
public TalentSubTree(XElement subTreeElement)
|
||||
{
|
||||
Identifier = subTreeElement.GetAttributeString("identifier", "");
|
||||
|
||||
DisplayName = TextManager.Get("talenttree." + Identifier, returnNull: true) ?? Identifier;
|
||||
|
||||
foreach (XElement talentOptionsElement in subTreeElement.GetChildElements("talentoptions"))
|
||||
{
|
||||
TalentOptionStages.Add(new TalentOption(talentOptionsElement, Identifier));
|
||||
@@ -196,6 +257,7 @@ namespace Barotrauma
|
||||
foreach (XElement talentOptionElement in talentOptionsElement.GetChildElements("talentoption"))
|
||||
{
|
||||
string identifier = talentOptionElement.GetAttributeString("identifier", string.Empty);
|
||||
|
||||
if (!TalentPrefab.TalentPrefabs.ContainsKey(identifier))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent tree \"{debugIdentifier}\" - could not find a talent with the identifier \"{identifier}\".");
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
|
||||
public enum ActionType
|
||||
{
|
||||
Always, OnPicked, OnUse, OnSecondaryUse,
|
||||
OnWearing, OnContaining, OnContained, OnNotContained,
|
||||
OnActive, OnFailure, OnBroken,
|
||||
OnFire, InWater, NotInWater,
|
||||
OnImpact,
|
||||
OnEating,
|
||||
OnDamaged,
|
||||
OnSevered,
|
||||
OnProduceSpawned,
|
||||
OnOpen, OnClose,
|
||||
Always = 0, OnPicked = 1, OnUse = 2, OnSecondaryUse = 3,
|
||||
OnWearing = 4, OnContaining = 5, OnContained = 6, OnNotContained = 7,
|
||||
OnActive = 8, OnFailure = 9, OnBroken = 10,
|
||||
OnFire = 11, InWater = 12, NotInWater = 13,
|
||||
OnImpact = 14,
|
||||
OnEating = 15,
|
||||
OnDamaged = 16,
|
||||
OnSevered = 17,
|
||||
OnProduceSpawned = 18,
|
||||
OnOpen = 19, OnClose = 20,
|
||||
OnDeath = OnBroken,
|
||||
OnSuccess,
|
||||
OnAbility,
|
||||
@@ -34,6 +34,7 @@
|
||||
OnAttack,
|
||||
OnAttackResult,
|
||||
OnAttacked,
|
||||
OnAttackedResult,
|
||||
OnGainSkillPoint,
|
||||
OnAllyGainSkillPoint,
|
||||
OnRepairComplete,
|
||||
@@ -57,7 +58,9 @@
|
||||
OnGainMissionMoney,
|
||||
OnItemDeconstructed,
|
||||
OnItemDeconstructedMaterial,
|
||||
OnItemDeconstructedRetainProbability,
|
||||
OnStopTinkering,
|
||||
OnItemPicked,
|
||||
AfterSubmarineAttacked,
|
||||
}
|
||||
|
||||
@@ -78,26 +81,37 @@
|
||||
BuffDurationMultiplier,
|
||||
DebuffDurationMultiplier,
|
||||
MedicalItemEffectivenessMultiplier,
|
||||
FlowResistance,
|
||||
// Combat
|
||||
AttackMultiplier,
|
||||
TeamAttackMultiplier,
|
||||
RangedAttackSpeed,
|
||||
TurretAttackSpeed,
|
||||
TurretPowerCostReduction,
|
||||
TurretChargeSpeed,
|
||||
MeleeAttackSpeed,
|
||||
MeleeAttackMultiplier,
|
||||
RangedAttackMultiplier,
|
||||
RangedSpreadReduction,
|
||||
// Utility
|
||||
RepairSpeed,
|
||||
DeconstructorSpeedMultiplier,
|
||||
TinkeringDuration,
|
||||
RepairToolStructureRepairMultiplier,
|
||||
RepairToolStructureDamageMultiplier,
|
||||
RepairToolDeattachTimeMultiplier,
|
||||
MaxRepairConditionMultiplier,
|
||||
IncreaseFabricationQuality,
|
||||
GeneticMaterialRefineBonus,
|
||||
GeneticMaterialTaintedProbabilityReductionOnCombine,
|
||||
SkillGainSpeed,
|
||||
// Misc
|
||||
ReputationGainMultiplier,
|
||||
MissionMoneyGainMultiplier,
|
||||
ExperienceGainMultiplier,
|
||||
MissionExperienceGainMultiplier,
|
||||
// these should be deprecated and moved to their own implementation, no sense making them share space with stat values
|
||||
Coathor,
|
||||
Coauthor,
|
||||
WarriorPoetMissionRuns,
|
||||
WarriorPoetEnemiesKilled,
|
||||
}
|
||||
@@ -113,7 +127,8 @@
|
||||
CanTinkerFabricatorsAndDeconstructors,
|
||||
TinkeringPowersDevices,
|
||||
GainSkillPastMaximum,
|
||||
RetainExperienceForNewCharacter
|
||||
RetainExperienceForNewCharacter,
|
||||
AllowSecondOrderedTarget,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ namespace Barotrauma
|
||||
case 0:
|
||||
|
||||
if (items.All(it => it.Removed || it.Condition <= 0.0f) &&
|
||||
requireKill.All(c => c.Removed || c.IsDead) &&
|
||||
requireKill.All(c => c.Removed || c.IsDead || (c.LockHands && c.Submarine == Submarine.MainSub)) &&
|
||||
requireRescue.All(c => c.Submarine?.Info.Type == SubmarineType.Player))
|
||||
{
|
||||
State = 1;
|
||||
|
||||
@@ -382,11 +382,11 @@ namespace Barotrauma
|
||||
State = newState;
|
||||
}
|
||||
|
||||
private bool CheckWinState() => !IsClient && (characters.All(m => !Survived(m)));
|
||||
private bool CheckWinState() => !IsClient && characters.All(m => DeadOrCaptured(m));
|
||||
|
||||
private bool Survived(Character character)
|
||||
private bool DeadOrCaptured(Character character)
|
||||
{
|
||||
return character != null && !character.Removed && !character.IsDead;
|
||||
return character != null && !character.Removed && (character.IsDead || (character.LockHands && character.Submarine == Submarine.MainSub));
|
||||
}
|
||||
|
||||
public override void End()
|
||||
|
||||
@@ -247,8 +247,8 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
var root = item.GetRootContainer() ?? item;
|
||||
if (root.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
|
||||
var root = item?.GetRootContainer() ?? item;
|
||||
if (root?.CurrentHull?.Submarine == null || (!root.CurrentHull.Submarine.AtEndExit && !root.CurrentHull.Submarine.AtStartExit) || item.Removed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,5 +92,12 @@ namespace Barotrauma.Extensions
|
||||
{
|
||||
return MathUtils.NearlyEqual(v.X, other.X) && MathUtils.NearlyEqual(v.Y, other.Y);
|
||||
}
|
||||
|
||||
public static Vector2 Pad(this Vector2 v, Vector4 padding)
|
||||
{
|
||||
v.X += padding.X + padding.Z;
|
||||
v.Y += padding.Y + padding.W;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public GeneticMaterial(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -85,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool CanBeCombinedWith(GeneticMaterial otherGeneticMaterial)
|
||||
{
|
||||
return !tainted && otherGeneticMaterial != null && !otherGeneticMaterial.tainted;
|
||||
return !tainted && otherGeneticMaterial != null && !otherGeneticMaterial.tainted && item.AllowDeconstruct && otherGeneticMaterial.item.AllowDeconstruct;
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
@@ -147,9 +146,12 @@ namespace Barotrauma.Items.Components
|
||||
public bool Combine(GeneticMaterial otherGeneticMaterial, Character user)
|
||||
{
|
||||
if (!CanBeCombinedWith(otherGeneticMaterial)) { return false; }
|
||||
|
||||
float conditionIncrease = Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
|
||||
conditionIncrease *= 1.0f + user.GetStatValue(StatTypes.GeneticMaterialRefineBonus);
|
||||
if (item.Prefab == otherGeneticMaterial.item.Prefab)
|
||||
{
|
||||
item.Condition = Math.Max(item.Condition, otherGeneticMaterial.item.Condition) + Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
|
||||
item.Condition = Math.Max(item.Condition, otherGeneticMaterial.item.Condition) + conditionIncrease;
|
||||
float taintedProbability = GetTaintedProbabilityOnRefine(user);
|
||||
if (taintedProbability >= Rand.Range(0.0f, 1.0f))
|
||||
{
|
||||
@@ -160,9 +162,14 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
item.Condition = otherGeneticMaterial.Item.Condition =
|
||||
(item.Condition + otherGeneticMaterial.Item.Condition) / 2.0f + Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
|
||||
(item.Condition + otherGeneticMaterial.Item.Condition) / 2.0f + conditionIncrease;
|
||||
item.OwnInventory?.TryPutItem(otherGeneticMaterial.Item, user: null);
|
||||
MakeTainted();
|
||||
item.AllowDeconstruct = false;
|
||||
otherGeneticMaterial.Item.AllowDeconstruct = false;
|
||||
if (GetTaintedProbabilityOnCombine(user) >= Rand.Range(0.0f, 1.0f))
|
||||
{
|
||||
MakeTainted();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +179,14 @@ namespace Barotrauma.Items.Components
|
||||
if (user == null) { return 1.0f; }
|
||||
float probability = MathHelper.Lerp(0.0f, 0.99f, item.Condition / 100.0f);
|
||||
probability *= MathHelper.Lerp(1.0f, 0.25f, DegreeOfSuccess(user));
|
||||
return probability;
|
||||
return MathHelper.Clamp(probability, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private float GetTaintedProbabilityOnCombine(Character user)
|
||||
{
|
||||
if (user == null) { return 1.0f; }
|
||||
float probability = 1.0f - user.GetStatValue(StatTypes.GeneticMaterialTaintedProbabilityReductionOnCombine);
|
||||
return MathHelper.Clamp(probability, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private void MakeTainted()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Abilities;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -73,12 +74,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (PickingTime > 0.0f)
|
||||
{
|
||||
var abilityPickingTime = new AbilityValueItem(PickingTime, item.Prefab);
|
||||
picker.CheckTalents(AbilityEffectType.OnItemPicked, abilityPickingTime);
|
||||
|
||||
if ((picker.PickingItem == null || picker.PickingItem == item) && PickingTime <= float.MaxValue)
|
||||
{
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, PickingTime));
|
||||
pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, abilityPickingTime.Value));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -523,7 +523,20 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new ISerializableEntity[] { targetStructure });
|
||||
FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
|
||||
targetStructure.AddDamage(sectionIndex, -StructureFixAmount * degreeOfSuccess, user);
|
||||
|
||||
float structureFixAmount = StructureFixAmount;
|
||||
if (structureFixAmount >= 0f)
|
||||
{
|
||||
structureFixAmount *= 1 + user.GetStatValue(StatTypes.RepairToolStructureRepairMultiplier);
|
||||
structureFixAmount *= 1 + item.GetQualityModifier(Quality.StatType.RepairToolStructureRepairMultiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
structureFixAmount *= 1 + user.GetStatValue(StatTypes.RepairToolStructureDamageMultiplier);
|
||||
structureFixAmount *= 1 + item.GetQualityModifier(Quality.StatType.RepairToolStructureDamageMultiplier);
|
||||
}
|
||||
|
||||
targetStructure.AddDamage(sectionIndex, -structureFixAmount * degreeOfSuccess, user);
|
||||
|
||||
//if the next section is small enough, apply the effect to it as well
|
||||
//(to make it easier to fix a small "left-over" section)
|
||||
@@ -535,7 +548,7 @@ namespace Barotrauma.Items.Components
|
||||
(nextSectionLength > 0 && nextSectionLength < Structure.WallSectionSize * 0.3f))
|
||||
{
|
||||
//targetStructure.HighLightSection(sectionIndex + i);
|
||||
targetStructure.AddDamage(sectionIndex + i, -StructureFixAmount * degreeOfSuccess);
|
||||
targetStructure.AddDamage(sectionIndex + i, -structureFixAmount * degreeOfSuccess);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -606,7 +619,8 @@ namespace Barotrauma.Items.Components
|
||||
levelResource.requiredItems.Any() &&
|
||||
levelResource.HasRequiredItems(user, addMessage: false))
|
||||
{
|
||||
levelResource.DeattachTimer += deltaTime;
|
||||
float addedDetachTime = deltaTime * (1f + user.GetStatValue(StatTypes.RepairToolDeattachTimeMultiplier)) * item.GetQualityModifier(Quality.StatType.RepairToolDeattachTimeMultiplier);
|
||||
levelResource.DeattachTimer += addedDetachTime;
|
||||
#if CLIENT
|
||||
Character.Controlled?.UpdateHUDProgressBar(
|
||||
this,
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Barotrauma.Items.Components
|
||||
public int Capacity
|
||||
{
|
||||
get { return capacity; }
|
||||
set { capacity = Math.Max(value, 1); }
|
||||
set { capacity = Math.Max(value, 0); }
|
||||
}
|
||||
|
||||
//how many items can be contained
|
||||
@@ -86,15 +86,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
[Editable]
|
||||
#endif
|
||||
[Serialize("0.0,0.0", false, description: "The position where the contained items get drawn at (offset from the upper left corner of the sprite in pixels).")]
|
||||
public Vector2 ItemPos { get; set; }
|
||||
|
||||
#if DEBUG
|
||||
[Editable]
|
||||
#endif
|
||||
[Serialize("0.0,0.0", false, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
|
||||
public Vector2 ItemInterval { get; set; }
|
||||
|
||||
@@ -329,11 +323,24 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return slotRestrictions.Any(s => s.MatchesItem(item));
|
||||
}
|
||||
|
||||
public bool CanBeContained(Item item, int index)
|
||||
{
|
||||
if (index < 0 || index >= capacity) { return false; }
|
||||
return slotRestrictions[index].MatchesItem(item);
|
||||
}
|
||||
|
||||
public bool CanBeContained(ItemPrefab itemPrefab)
|
||||
{
|
||||
return slotRestrictions.Any(s => s.MatchesItem(itemPrefab));
|
||||
}
|
||||
|
||||
public bool CanBeContained(ItemPrefab itemPrefab, int index)
|
||||
{
|
||||
if (index < 0 || index >= capacity) { return false; }
|
||||
return slotRestrictions[index].MatchesItem(itemPrefab);
|
||||
}
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
|
||||
@@ -256,6 +256,17 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (user != null && !user.Removed)
|
||||
{
|
||||
var deconstructItemRetainProbability = new AbilityValueItem(0f, targetItem.Prefab);
|
||||
user.CheckTalents(AbilityEffectType.OnItemDeconstructedRetainProbability, deconstructItemRetainProbability);
|
||||
|
||||
if (deconstructItemRetainProbability.Value > Rand.Range(0f, 1f, Rand.RandSync.Unsynced))
|
||||
{
|
||||
allowRemove = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem.AllowDeconstruct && allowRemove)
|
||||
{
|
||||
//drop all items that are inside the deconstructed item
|
||||
|
||||
@@ -24,12 +24,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Character user;
|
||||
|
||||
public float FabricationSpeedMultiplier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private ItemContainer inputContainer, outputContainer;
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
@@ -249,7 +243,6 @@ namespace Barotrauma.Items.Components
|
||||
var availableIngredients = GetAvailableIngredients();
|
||||
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
|
||||
{
|
||||
FabricationSpeedMultiplier = 1f;
|
||||
CancelFabricating();
|
||||
return;
|
||||
}
|
||||
@@ -286,8 +279,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (powerConsumption <= 0) { Voltage = 1.0f; }
|
||||
|
||||
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f) * FabricationSpeedMultiplier;
|
||||
FabricationSpeedMultiplier = 1f;
|
||||
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f);
|
||||
|
||||
UpdateRequiredTimeProjSpecific();
|
||||
|
||||
@@ -328,13 +320,21 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
var fabricationValueItem = new AbilityValueItem(fabricatedItem.Amount, fabricatedItem.TargetItem);
|
||||
|
||||
if (user != null)
|
||||
int quality = 0;
|
||||
if (user?.Info != null)
|
||||
{
|
||||
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == user.TeamID))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationValueItem);
|
||||
}
|
||||
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationValueItem);
|
||||
|
||||
float floatQuality = 0.0f;
|
||||
foreach (string tag in fabricatedItem.TargetItem.Tags)
|
||||
{
|
||||
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
|
||||
}
|
||||
quality = (int)floatQuality;
|
||||
}
|
||||
|
||||
var tempUser = user;
|
||||
@@ -343,12 +343,20 @@ namespace Barotrauma.Items.Components
|
||||
if (i < amountFittingContainer)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
onItemSpawned(spawnedItem, tempUser);
|
||||
spawnedItem.Quality = quality;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
|
||||
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
|
||||
onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
onItemSpawned(spawnedItem, tempUser);
|
||||
spawnedItem.Quality = quality;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.CurrentHull.WaterVolume += currFlow;
|
||||
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
|
||||
|
||||
Voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public void InfectBallast(string identifier, bool allowMultiplePerShip = false)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Quality : ItemComponent
|
||||
{
|
||||
public const int MaxQuality = 3;
|
||||
|
||||
public enum StatType
|
||||
{
|
||||
Condition,
|
||||
ExplosionRadius,
|
||||
ExplosionDamage,
|
||||
RepairSpeed,
|
||||
RepairToolStructureRepairMultiplier,
|
||||
RepairToolStructureDamageMultiplier,
|
||||
RepairToolDeattachTimeMultiplier,
|
||||
// unused as of now
|
||||
AttackMultiplier,
|
||||
AttackSpeedMultiplier,
|
||||
ForceDoorsOpenSpeedMultiplier,
|
||||
RangedSpreadReduction,
|
||||
ChargeSpeedMultiplier,
|
||||
MovementSpeedMultiplier,
|
||||
// generic stats to be used for various needs, declared just in case (localization)
|
||||
EffectivenessMultiplier,
|
||||
PowerOutputMultiplier,
|
||||
ConsumptionReductionMultiplier,
|
||||
}
|
||||
|
||||
private readonly Dictionary<StatType, float> statValues = new Dictionary<StatType, float>();
|
||||
|
||||
private int qualityLevel;
|
||||
|
||||
[Serialize(0, false)]
|
||||
public int QualityLevel
|
||||
{
|
||||
get { return qualityLevel; }
|
||||
set { qualityLevel = MathHelper.Clamp(value, 0, MaxQuality); }
|
||||
}
|
||||
|
||||
public Quality(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
{
|
||||
case "stattype":
|
||||
case "statvalue":
|
||||
case "qualitystat":
|
||||
string statTypeString = subElement.GetAttributeString("stattype", "");
|
||||
if (!Enum.TryParse(statTypeString, true, out StatType statType))
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in item (" + item.prefab.Identifier + ")");
|
||||
}
|
||||
float statValue = subElement.GetAttributeFloat("value", 0f);
|
||||
statValues.TryAdd(statType, statValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float GetValue(StatType statType)
|
||||
{
|
||||
if (!statValues.ContainsKey(statType)) { return 0.0f; }
|
||||
return statValues[statType] * qualityLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ namespace Barotrauma.Items.Components
|
||||
public RemoteController(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
DrawHudWhenEquipped = false;
|
||||
}
|
||||
|
||||
public override bool Select(Character character)
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private string header;
|
||||
private readonly string header;
|
||||
|
||||
private float deteriorationTimer;
|
||||
private float deteriorateAlwaysResetTimer;
|
||||
@@ -182,6 +182,10 @@ namespace Barotrauma.Items.Components
|
||||
if (Rand.Range(0.0f, 0.5f) < RepairDegreeOfSuccess(character, requiredSkills)) { return true; }
|
||||
|
||||
ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
|
||||
if (bestRepairItem != null && bestRepairItem.GetComponent<Holdable>() is Holdable h)
|
||||
{
|
||||
h.ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -217,6 +221,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
GameServer.Log($"{GameServer.CharacterLogName(character)} failed to {(action == FixActions.Sabotage ? "sabotage" : "repair")} {item.Name}", ServerLog.MessageType.ItemInteraction);
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, character.ID });
|
||||
if (bestRepairItem != null && bestRepairItem.GetComponent<Holdable>() is Holdable h)
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(bestRepairItem, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, h, character.ID });
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -243,7 +252,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
return true;
|
||||
|
||||
Item GetBestRepairItem(Character character)
|
||||
static Item GetBestRepairItem(Character character)
|
||||
{
|
||||
return character.HeldItems.OrderByDescending(i => i.Prefab.AddedRepairSpeedMultiplier).FirstOrDefault();
|
||||
}
|
||||
@@ -386,6 +395,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
|
||||
fixDuration /= 1 + CurrentFixer.GetStatValue(StatTypes.RepairSpeed) + currentRepairItem?.Prefab.AddedRepairSpeedMultiplier ?? 0f;
|
||||
fixDuration /= 1 + item.GetQualityModifier(Quality.StatType.RepairSpeed);
|
||||
|
||||
item.MaxRepairConditionMultiplier = 1 + CurrentFixer.GetStatValue(StatTypes.MaxRepairConditionMultiplier);
|
||||
|
||||
if (currentFixerAction == FixActions.Repair)
|
||||
{
|
||||
|
||||
@@ -383,6 +383,10 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
float chargeDeltaTime = tryingToCharge ? deltaTime : -deltaTime;
|
||||
if (chargeDeltaTime > 0f && user != null)
|
||||
{
|
||||
chargeDeltaTime *= 1f + user.GetStatValue(StatTypes.TurretChargeSpeed);
|
||||
}
|
||||
currentChargeTime = Math.Clamp(currentChargeTime + chargeDeltaTime, 0f, MaxChargeTime);
|
||||
}
|
||||
tryingToCharge = false;
|
||||
|
||||
@@ -285,7 +285,7 @@ namespace Barotrauma.Items.Components
|
||||
int i = 0;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
if (subElement.Attribute("texture") == null)
|
||||
|
||||
@@ -97,13 +97,15 @@ namespace Barotrauma
|
||||
|
||||
private Dictionary<string, Connection> connections;
|
||||
|
||||
private List<Repairable> repairables;
|
||||
private readonly List<Repairable> repairables;
|
||||
|
||||
private Queue<float> impactQueue = new Queue<float>();
|
||||
private Quality qualityComponent;
|
||||
|
||||
private readonly Queue<float> impactQueue = new Queue<float>();
|
||||
|
||||
//a dictionary containing lists of the status effects in all the components of the item
|
||||
private bool[] hasStatusEffectsOfType;
|
||||
private Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
|
||||
private readonly bool[] hasStatusEffectsOfType;
|
||||
private readonly Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; protected set; }
|
||||
|
||||
@@ -447,7 +449,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public bool IsFullCondition => MathUtils.NearlyEqual(Condition, MaxCondition);
|
||||
public float MaxCondition => Prefab.Health * healthMultiplier;
|
||||
public float MaxCondition => Prefab.Health * healthMultiplier * maxRepairConditionMultiplier * (1.0f + GetQualityModifier(Items.Components.Quality.StatType.Condition));
|
||||
public float ConditionPercentage => MathUtils.Percentage(Condition, MaxCondition);
|
||||
|
||||
private float offsetOnSelectedMultiplier = 1.0f;
|
||||
@@ -465,12 +467,18 @@ namespace Barotrauma
|
||||
public float HealthMultiplier
|
||||
{
|
||||
get => healthMultiplier;
|
||||
set
|
||||
{
|
||||
healthMultiplier = value;
|
||||
}
|
||||
set { healthMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity); }
|
||||
}
|
||||
|
||||
|
||||
private float maxRepairConditionMultiplier = 1.0f;
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
public float MaxRepairConditionMultiplier
|
||||
{
|
||||
get => maxRepairConditionMultiplier;
|
||||
set { maxRepairConditionMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity); }
|
||||
}
|
||||
|
||||
//the default value should be Prefab.Health, but because we can't use it in the attribute,
|
||||
//we'll just use NaN (which does nothing) and set the default value in the constructor/load
|
||||
[Serialize(float.NaN, false), Editable]
|
||||
@@ -618,6 +626,21 @@ namespace Barotrauma
|
||||
get { return Prefab.UseInHealthInterface; }
|
||||
}
|
||||
|
||||
public int Quality
|
||||
{
|
||||
get
|
||||
{
|
||||
return qualityComponent?.QualityLevel ?? 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (qualityComponent != null)
|
||||
{
|
||||
qualityComponent.QualityLevel = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool InWater
|
||||
{
|
||||
get
|
||||
@@ -933,6 +956,8 @@ namespace Barotrauma
|
||||
ownInventory = itemContainer.Inventory;
|
||||
}
|
||||
|
||||
qualityComponent = GetComponent<Quality>();
|
||||
|
||||
InitProjSpecific();
|
||||
|
||||
if (callOnItemLoaded)
|
||||
@@ -1122,6 +1147,11 @@ namespace Barotrauma
|
||||
if (!componentsByType.ContainsKey(typeof(T))) { return Enumerable.Empty<T>(); }
|
||||
return components.Where(c => c is T).Cast<T>();
|
||||
}
|
||||
|
||||
public float GetQualityModifier(Quality.StatType statType)
|
||||
{
|
||||
return GetComponent<Quality>()?.GetValue(statType) ?? 0.0f;
|
||||
}
|
||||
|
||||
public void RemoveContained(Item contained)
|
||||
{
|
||||
|
||||
@@ -47,14 +47,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (ItemOwnsSelf(item)) { return false; }
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
if (!container.CanBeContained(item)) { return false; }
|
||||
if (!container.CanBeContained(item, i)) { return false; }
|
||||
return item != null && slots[i].CanBePut(item, ignoreCondition) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
public override bool CanBePutInSlot(ItemPrefab itemPrefab, int i, float? condition)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return false; }
|
||||
if (!container.CanBeContained(itemPrefab)) { return false; }
|
||||
if (!container.CanBeContained(itemPrefab, i)) { return false; }
|
||||
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition) && slots[i].ItemCount < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (itemPrefab == null) { return 0; }
|
||||
if (i < 0 || i >= slots.Length) { return 0; }
|
||||
if (!container.CanBeContained(itemPrefab)) { return 0; }
|
||||
if (!container.CanBeContained(itemPrefab, i)) { return 0; }
|
||||
return slots[i].HowManyCanBePut(itemPrefab, maxStackSize: Math.Min(itemPrefab.MaxStackSize, container.GetMaxStackSize(i)), condition);
|
||||
}
|
||||
|
||||
|
||||
@@ -370,6 +370,9 @@ namespace Barotrauma
|
||||
[Serialize(false, false, description: "Hides the condition bar displayed at the bottom of the inventory slot the item is in.")]
|
||||
public bool HideConditionBar { get; set; }
|
||||
|
||||
[Serialize(false, false, description: "Hides the condition displayed in the item's tooltip.")]
|
||||
public bool HideConditionInTooltip { get; set; }
|
||||
|
||||
//if true and the item has trigger areas defined, characters need to be within the trigger to interact with the item
|
||||
//if false, trigger areas define areas that can be used to highlight the item
|
||||
[Serialize(true, false)]
|
||||
@@ -1164,6 +1167,8 @@ namespace Barotrauma
|
||||
DefaultPrice ??= new PriceInfo(GetMinPrice() ?? 0, false);
|
||||
}
|
||||
|
||||
HideConditionInTooltip = element.GetAttributeBool("hideconditionintooltip", HideConditionBar);
|
||||
|
||||
//backwards compatibility
|
||||
if (categoryStr.Equals("Thalamus", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
||||
@@ -128,6 +128,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float displayRange = Attack.Range;
|
||||
if (damageSource is Item sourceItem)
|
||||
{
|
||||
displayRange *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionRadius);
|
||||
Attack.DamageMultiplier *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionDamage);
|
||||
}
|
||||
|
||||
Vector2 cameraPos = GameMain.GameScreen.Cam.Position;
|
||||
float cameraDist = Vector2.Distance(cameraPos, worldPosition) / 2.0f;
|
||||
@@ -142,7 +147,7 @@ namespace Barotrauma
|
||||
|
||||
if (displayRange < 0.1f) { return; }
|
||||
|
||||
if (Attack.GetStructureDamage(1.0f) > 0.0f || Attack.GetLevelWallDamage(1.0f) > 0.0f)
|
||||
if (!MathUtils.NearlyEqual(Attack.GetStructureDamage(1.0f), 0.0f) || !MathUtils.NearlyEqual(Attack.GetLevelWallDamage(1.0f), 0.0f))
|
||||
{
|
||||
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker);
|
||||
}
|
||||
@@ -211,9 +216,9 @@ namespace Barotrauma
|
||||
float dist = Vector2.Distance(item.WorldPosition, worldPosition);
|
||||
float itemRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
|
||||
dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(itemRadius));
|
||||
if (dist > Attack.Range) { continue; }
|
||||
if (dist > displayRange) { continue; }
|
||||
|
||||
if (dist < Attack.Range * 0.5f && applyFireEffects && !item.FireProof && ignoreFireEffectsForTags.None(t => item.HasTag(t)))
|
||||
if (dist < displayRange * 0.5f && applyFireEffects && !item.FireProof && ignoreFireEffectsForTags.None(t => item.HasTag(t)))
|
||||
{
|
||||
//don't apply OnFire effects if the item is inside a fireproof container
|
||||
//(or if it's inside a container that's inside a fireproof container, etc)
|
||||
@@ -240,7 +245,7 @@ namespace Barotrauma
|
||||
|
||||
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
|
||||
{
|
||||
float distFactor = 1.0f - dist / Attack.Range;
|
||||
float distFactor = 1.0f - dist / displayRange;
|
||||
float damageAmount = Attack.GetItemDamage(1.0f) * item.Prefab.ExplosionDamageMultiplier;
|
||||
|
||||
Vector2 explosionPos = worldPosition;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
@@ -8,6 +9,7 @@ using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using File = Barotrauma.IO.File;
|
||||
using FileStream = Barotrauma.IO.FileStream;
|
||||
using Path = Barotrauma.IO.Path;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -18,11 +20,12 @@ namespace Barotrauma
|
||||
public static readonly XmlReaderSettings ReaderSettings = new XmlReaderSettings
|
||||
{
|
||||
DtdProcessing = DtdProcessing.Prohibit,
|
||||
XmlResolver = null
|
||||
XmlResolver = null,
|
||||
IgnoreWhitespace = true,
|
||||
};
|
||||
|
||||
public static XmlReader CreateReader(System.IO.Stream stream)
|
||||
=> XmlReader.Create(stream, ReaderSettings);
|
||||
|
||||
public static XmlReader CreateReader(System.IO.Stream stream, string baseUri = "")
|
||||
=> XmlReader.Create(stream, ReaderSettings, baseUri);
|
||||
|
||||
public static XDocument TryLoadXml(System.IO.Stream stream)
|
||||
{
|
||||
@@ -52,8 +55,8 @@ namespace Barotrauma
|
||||
{
|
||||
ToolBox.IsProperFilenameCase(filePath);
|
||||
using FileStream stream = File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
|
||||
using XmlReader reader = CreateReader(stream);
|
||||
doc = XDocument.Load(reader);
|
||||
using XmlReader reader = CreateReader(stream, Path.GetFullPath(filePath));
|
||||
doc = XDocument.Load(reader, LoadOptions.SetBaseUri);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -79,7 +82,7 @@ namespace Barotrauma
|
||||
try
|
||||
{
|
||||
using FileStream stream = File.Open(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
|
||||
using XmlReader reader = CreateReader(stream);
|
||||
using XmlReader reader = CreateReader(stream, Path.GetFullPath(filePath));
|
||||
doc = XDocument.Load(reader);
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -60,6 +60,8 @@ namespace Barotrauma
|
||||
// Only used by conditionals targeting an item. By default, containers check the parent item. This allows you to check the grandparent instead.
|
||||
public readonly bool TargetGrandParent;
|
||||
|
||||
public readonly bool TargetContainedItem;
|
||||
|
||||
// Remove this after refactoring
|
||||
public static bool IsValid(XAttribute attribute)
|
||||
{
|
||||
@@ -112,6 +114,7 @@ namespace Barotrauma
|
||||
TargetContainer = attribute.Parent.GetAttributeBool("targetcontainer", false);
|
||||
TargetSelf = attribute.Parent.GetAttributeBool("targetself", false);
|
||||
TargetGrandParent = attribute.Parent.GetAttributeBool("targetgrandparent", false);
|
||||
TargetContainedItem = attribute.Parent.GetAttributeBool("targetcontaineditem", false);
|
||||
|
||||
if (!Enum.TryParse(AttributeName, true, out Type))
|
||||
{
|
||||
@@ -171,6 +174,22 @@ namespace Barotrauma
|
||||
|
||||
public bool Matches(ISerializableEntity target)
|
||||
{
|
||||
if (TargetContainedItem)
|
||||
{
|
||||
if (target is Item item)
|
||||
{
|
||||
return item.ContainedItems.Any(it => Matches(it));
|
||||
}
|
||||
else if (target is Items.Components.ItemComponent ic)
|
||||
{
|
||||
return ic.Item.ContainedItems.Any(it => Matches(it));
|
||||
}
|
||||
else if (target is Character character)
|
||||
{
|
||||
return character.Inventory != null && character.Inventory.AllItems.Any(it => Matches(it));
|
||||
}
|
||||
}
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case ConditionType.PropertyValue:
|
||||
|
||||
@@ -315,7 +315,11 @@ namespace Barotrauma.IO
|
||||
return System.IO.File.GetLastWriteTime(path);
|
||||
}
|
||||
|
||||
public static FileStream Open(string path, System.IO.FileMode mode, System.IO.FileAccess access = System.IO.FileAccess.ReadWrite)
|
||||
public static FileStream Open(
|
||||
string path,
|
||||
System.IO.FileMode mode,
|
||||
System.IO.FileAccess access = System.IO.FileAccess.ReadWrite,
|
||||
System.IO.FileShare? share = null)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
@@ -331,10 +335,12 @@ namespace Barotrauma.IO
|
||||
}
|
||||
break;
|
||||
}
|
||||
return new FileStream(path, System.IO.File.Open(path, mode,
|
||||
access =
|
||||
!Validation.CanWrite(path, false) ?
|
||||
System.IO.FileAccess.Read :
|
||||
access));
|
||||
access;
|
||||
var shareVal = share ?? (access == System.IO.FileAccess.Read ? System.IO.FileShare.Read : System.IO.FileShare.None);
|
||||
return new FileStream(path, System.IO.File.Open(path, mode, access, shareVal));
|
||||
}
|
||||
|
||||
public static FileStream OpenRead(string path)
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.1500.3.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Additions and changes:
|
||||
- More talents and talent-related items (all talent trees now functional and most of the talents implemented).
|
||||
|
||||
Changes:
|
||||
- Ignore hidden afflictions when determining treatment suggestions to show in the health interface.
|
||||
- Visualize leaks on the status monitor's hull condition tab (unstable only).
|
||||
- Added "condition_out" output to outpost O2 generator (unstable only).
|
||||
|
||||
Fixes:
|
||||
- Fixed crashing when reloading sprites or resetting to prefab in the sub editor.
|
||||
- Fixed ability to combine unidentified genetic materials with other genetic materials (unstable only).
|
||||
- Organ damage doesn't cause concussions (unstable only).
|
||||
- Fixed talent menu being accessible if you leave it open and switch to a game mode where it shouldn't be accessible (unstable only).
|
||||
- Fixed ability to contain items other than batteries in cargo scooter's battery slot (unstable only).
|
||||
- Damaging the mudraptor beak given by mudraptor genes damages the head instead of torso, added damage protection to the beak (unstable only).
|
||||
- Items that are set to be hidden in menus aren't shown in the status monitor's item finder (unstable only).
|
||||
- Fixed status monitor's item finder not showing wearable items (unstable only).
|
||||
- Fixed "in use by xxxx" warning being always visible when using a Reactor PDA (unstable only).
|
||||
- Fixed Reactor PDA rendering over the command interface (unstable only).
|
||||
- Fixed assault rifle crosshair being drawn when it's in the bag slot (unstable only).
|
||||
- Fixed equip buttons not being drawn on equipped items that can only be put to other equip slots, but not on the non-limb slots (e.g. assault rifle).
|
||||
|
||||
Modding:
|
||||
- Option to make property conditionals target contained items using the attribute targetcontaineditem="true".
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.1500.2.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
autorestart="false"
|
||||
LevelDifficulty="20"
|
||||
AllowedRandomMissionTypes="Random,Salvage,Monster,Cargo,Combat"
|
||||
AllowedClientNameChars="32-33,38-46,48-57,65-90,91-91,93-93,95-122,192-255,384-591,1024-1279,19968-40959,13312-19903,131072-15043983,15043985-173791,173824-178207,178208-183983,63744-64255,194560-195103"
|
||||
AllowedClientNameChars="32-33,38-46,48-57,65-90,91-91,93-93,95-122,192-255,384-591,1024-1279,19968-21327,21329-40959,13312-19903,131072-173791,173824-178207,178208-183983,63744-64255,194560-195103"
|
||||
ServerMessage=""
|
||||
tickrate="20"
|
||||
randomizeseed="True"
|
||||
|
||||
Reference in New Issue
Block a user