Unstable 0.1500.1.0 (BaroDev edition)
This commit is contained in:
@@ -1915,12 +1915,12 @@ namespace Barotrauma
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(Character, new object[]
|
||||
{
|
||||
Networking.NetEntityEvent.Type.SetAttackTarget,
|
||||
attackingLimb,
|
||||
(damageTarget as Entity)?.ID ?? Entity.NullEntityID,
|
||||
damageTarget is Character character && targetLimb != null ? Array.IndexOf(character.AnimController.Limbs, targetLimb) : 0,
|
||||
SimPosition.X,
|
||||
SimPosition.Y
|
||||
Networking.NetEntityEvent.Type.SetAttackTarget,
|
||||
attackingLimb,
|
||||
(damageTarget as Entity)?.ID ?? Entity.NullEntityID,
|
||||
damageTarget is Character character && targetLimb != null ? Array.IndexOf(character.AnimController.Limbs, targetLimb) : 0,
|
||||
SimPosition.X,
|
||||
SimPosition.Y
|
||||
});
|
||||
#else
|
||||
Character.PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
|
||||
|
||||
+3
-2
@@ -588,8 +588,9 @@ namespace Barotrauma
|
||||
// assume that it's required for the stun effect
|
||||
// as we can't check the status effect conditions here.
|
||||
var mobileBatteryTag = "mobilebattery";
|
||||
var containers = weapon.Item.Components.Where(ic => ic is ItemContainer container &&
|
||||
container.ContainableItems.Any(containable => containable.Identifiers.Any(id => id.Equals(mobileBatteryTag))));
|
||||
var containers = weapon.Item.Components.Where(ic =>
|
||||
ic is ItemContainer container &&
|
||||
container.ContainableItemIdentifiers.Contains(mobileBatteryTag));
|
||||
// If there's no such container, assume that the melee weapon can stun without a battery.
|
||||
return containers.None() || containers.Any(container =>
|
||||
(container as ItemContainer)?.Inventory.AllItems.Any(i => i != null && i.HasTag(mobileBatteryTag) && i.Condition > 0.0f) ?? false);
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace Barotrauma
|
||||
|
||||
public virtual void UpdateAnim(float deltaTime) { }
|
||||
|
||||
public virtual void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f) { }
|
||||
public virtual void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f, bool aimingMelee = false) { }
|
||||
|
||||
public virtual void DragCharacter(Character target, float deltaTime) { }
|
||||
|
||||
|
||||
@@ -390,11 +390,17 @@ namespace Barotrauma
|
||||
}
|
||||
if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
|
||||
{
|
||||
bool CanBeSevered(LimbJoint j) => !j.IsSevered && j.CanBeSevered && j.LimbA != null && !j.LimbA.IsSevered && j.LimbB != null && !j.LimbB.IsSevered;
|
||||
static bool CanBeSevered(LimbJoint j) => !j.IsSevered && j.CanBeSevered && j.LimbA != null && !j.LimbA.IsSevered && j.LimbB != null && !j.LimbB.IsSevered;
|
||||
//keep severing joints until there is only one limb left
|
||||
var nonSeveredJoints = target.AnimController.LimbJoints.Where(CanBeSevered);
|
||||
if (nonSeveredJoints.None())
|
||||
{
|
||||
//small monsters don't eat the contents of the character's inventory
|
||||
if (Mass < target.AnimController.Mass)
|
||||
{
|
||||
target.Inventory?.AllItemsMod.ForEach(it => it?.Drop(dropper: null));
|
||||
}
|
||||
|
||||
//only one limb left, the character is now full eaten
|
||||
Entity.Spawner?.AddToRemoveQueue(target);
|
||||
|
||||
|
||||
+13
-2
@@ -150,6 +150,13 @@ namespace Barotrauma
|
||||
private float upperLegLength = 0.0f, lowerLegLength = 0.0f;
|
||||
|
||||
private bool aiming;
|
||||
private bool wasAiming;
|
||||
|
||||
private bool aimingMelee;
|
||||
private bool wasAimingMelee;
|
||||
|
||||
public bool IsAiming => wasAiming;
|
||||
public bool IsAimingMelee => wasAimingMelee;
|
||||
|
||||
private readonly float movementLerp;
|
||||
|
||||
@@ -532,7 +539,10 @@ namespace Barotrauma
|
||||
limb.Disabled = false;
|
||||
}
|
||||
|
||||
wasAiming = aiming;
|
||||
aiming = false;
|
||||
wasAimingMelee = aimingMelee;
|
||||
aimingMelee = false;
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) return;
|
||||
}
|
||||
|
||||
@@ -1718,7 +1728,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
//TODO: refactor this method, it's way too convoluted
|
||||
public override void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f)
|
||||
public override void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f, bool aimingMelee = false)
|
||||
{
|
||||
if (character.Stun > 0.0f || character.IsIncapacitated)
|
||||
{
|
||||
@@ -1748,6 +1758,8 @@ namespace Barotrauma
|
||||
|
||||
Holdable holdable = item.GetComponent<Holdable>();
|
||||
|
||||
this.aimingMelee = aimingMelee;
|
||||
|
||||
if (!isClimbing && !usingController && character.Stun <= 0.0f && aim && itemPos != Vector2.Zero && !character.IsIncapacitated)
|
||||
{
|
||||
Vector2 mousePos = ConvertUnits.ToSimUnits(character.SmoothedCursorPosition);
|
||||
@@ -1771,7 +1783,6 @@ namespace Barotrauma
|
||||
|
||||
aiming = true;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -395,12 +395,18 @@ namespace Barotrauma
|
||||
|
||||
if (character.IsHusk && character.Params.UseHuskAppendage)
|
||||
{
|
||||
bool inEditor = false;
|
||||
#if CLIENT
|
||||
inEditor = Screen.Selected == GameMain.CharacterEditorScreen;
|
||||
#endif
|
||||
|
||||
var characterPrefab = CharacterPrefab.FindByFilePath(character.ConfigPath);
|
||||
if (characterPrefab?.XDocument != null)
|
||||
{
|
||||
var mainElement = characterPrefab.XDocument.Root.IsOverride() ? characterPrefab.XDocument.Root.FirstElement() : characterPrefab.XDocument.Root;
|
||||
foreach (var huskAppendage in mainElement.GetChildElements("huskappendage"))
|
||||
{
|
||||
if (!inEditor && huskAppendage.GetAttributeBool("onlyfromafflictions", false)) { continue; }
|
||||
AfflictionHusk.AttachHuskAppendage(character, huskAppendage.GetAttributeString("affliction", string.Empty), huskAppendage, ragdoll: this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ namespace Barotrauma
|
||||
public enum HitDetection
|
||||
{
|
||||
Distance,
|
||||
Contact
|
||||
Contact,
|
||||
None
|
||||
}
|
||||
|
||||
public enum AttackContext
|
||||
@@ -74,20 +75,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
class AttackData
|
||||
{
|
||||
public float DamageMultiplier { get; set; } = 1f;
|
||||
public float AddedPenetration { get; set; } = 0f;
|
||||
public List<Affliction> Afflictions { get; set; }
|
||||
public Attack SourceAttack { get; }
|
||||
|
||||
public AttackData(Attack sourceAttack)
|
||||
{
|
||||
SourceAttack = sourceAttack;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
partial class Attack : ISerializableEntity
|
||||
{
|
||||
[Serialize(AttackContext.Any, true, description: "The attack will be used only in this context."), Editable]
|
||||
@@ -466,7 +453,7 @@ namespace Barotrauma
|
||||
|
||||
DamageParticles(deltaTime, worldPosition);
|
||||
|
||||
var attackResult = target.AddDamage(attacker, worldPosition, this, deltaTime, playSound);
|
||||
var attackResult = target?.AddDamage(attacker, worldPosition, this, deltaTime, playSound) ?? new AttackResult();
|
||||
var effectType = attackResult.Damage > 0.0f ? ActionType.OnUse : ActionType.OnFailure;
|
||||
if (targetCharacter != null && targetCharacter.IsDead)
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Abilities;
|
||||
#if SERVER
|
||||
using System.Text;
|
||||
#endif
|
||||
@@ -1366,7 +1367,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<Item> wearableItems = new List<Item>();
|
||||
|
||||
public float GetSkillLevel(string skillIdentifier)
|
||||
{
|
||||
@@ -2075,11 +2075,10 @@ namespace Barotrauma
|
||||
return SelectedCharacter == owner && owner.CanInventoryBeAccessed;
|
||||
}
|
||||
|
||||
if (inventory.Owner is Item)
|
||||
if (inventory.Owner is Item item)
|
||||
{
|
||||
var owner = (Item)inventory.Owner;
|
||||
if (!CanInteractWith(owner) && !owner.linkedTo.Any(lt => lt is Item item && item.DisplaySideBySideWhenLinked && CanInteractWith(item))) { return false; }
|
||||
ItemContainer container = owner.GetComponents<ItemContainer>().FirstOrDefault(ic => ic.Inventory == inventory);
|
||||
if (!CanInteractWith(item) && !item.linkedTo.Any(lt => lt is Item item && item.DisplaySideBySideWhenLinked && CanInteractWith(item))) { return false; }
|
||||
ItemContainer container = item.GetComponents<ItemContainer>().FirstOrDefault(ic => ic.Inventory == inventory);
|
||||
if (container != null && !container.HasRequiredItems(this, addMessage: false)) { return false; }
|
||||
}
|
||||
return true;
|
||||
@@ -2218,6 +2217,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem == item ||
|
||||
HeldItems.Any(it => it.GetComponent<RemoteController>()?.TargetItem == item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.InteractDistance == 0.0f && !item.Prefab.Triggers.Any()) { return false; }
|
||||
|
||||
Pickable pickableComponent = item.GetComponent<Pickable>();
|
||||
@@ -3355,10 +3360,18 @@ namespace Barotrauma
|
||||
|
||||
float attackImpulse = attack.TargetImpulse + attack.TargetForce * deltaTime;
|
||||
|
||||
AttackData attackData = new AttackData(attack);
|
||||
attacker.CheckTalents(AbilityEffectType.OnAttack, attackData);
|
||||
CheckTalents(AbilityEffectType.OnAttacked, attackData);
|
||||
attackData.DamageMultiplier *= (1 + attacker.GetStatValue(StatTypes.AttackMultiplier));
|
||||
AbilityAttackData attackData = new AbilityAttackData(attack, this);
|
||||
if (attacker != null)
|
||||
{
|
||||
attackData.Attacker = attacker;
|
||||
attacker.CheckTalents(AbilityEffectType.OnAttack, attackData);
|
||||
CheckTalents(AbilityEffectType.OnAttacked, attackData);
|
||||
attackData.DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.AttackMultiplier);
|
||||
if (attacker.TeamID == TeamID)
|
||||
{
|
||||
attackData.DamageMultiplier *= 1 + attacker.GetStatValue(StatTypes.TeamAttackMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<Affliction> attackAfflictions;
|
||||
|
||||
@@ -3495,6 +3508,7 @@ namespace Barotrauma
|
||||
{
|
||||
attackerCrewmember.CheckTalents(AbilityEffectType.OnCrewKillCharacter, target);
|
||||
}
|
||||
CheckTalents(AbilityEffectType.OnKillCharacter, target);
|
||||
|
||||
if (!IsOnPlayerTeam) { return; }
|
||||
if (GameMain.Config.KilledCreatures.Any(name => name.Equals(target.SpeciesName, StringComparison.OrdinalIgnoreCase))) { return; }
|
||||
@@ -3653,10 +3667,7 @@ namespace Barotrauma
|
||||
if (statusEffect.type != actionType) { continue; }
|
||||
if (statusEffect.type == ActionType.OnDamaged)
|
||||
{
|
||||
if (statusEffect.AllowedAfflictions != null && (LastDamage.Afflictions == null || LastDamage.Afflictions.None(a => statusEffect.AllowedAfflictions.Contains(a.Prefab.AfflictionType) || statusEffect.AllowedAfflictions.Contains(a.Prefab.Identifier))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!statusEffect.HasRequiredAfflictions(LastDamage)) { continue; }
|
||||
if (statusEffect.OnlyPlayerTriggered)
|
||||
{
|
||||
if (LastAttacker == null || !LastAttacker.IsPlayer)
|
||||
@@ -3719,7 +3730,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void Implode(bool isNetworkMessage = false)
|
||||
public void Implode(bool isNetworkMessage = false)
|
||||
{
|
||||
if (CharacterHealth.Unkillable || GodMode || IsDead) { return; }
|
||||
|
||||
@@ -3829,7 +3840,7 @@ namespace Barotrauma
|
||||
if (info != null)
|
||||
{
|
||||
info.CauseOfDeath = CauseOfDeath;
|
||||
info.ResetSavedStatValues();
|
||||
info.MissionsCompletedSinceDeath = 0;
|
||||
}
|
||||
AnimController.movement = Vector2.Zero;
|
||||
AnimController.TargetMovement = Vector2.Zero;
|
||||
@@ -4434,7 +4445,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private StatTypes GetSkillStatType(string skillIdentifier)
|
||||
public static StatTypes GetSkillStatType(string skillIdentifier)
|
||||
{
|
||||
// Using this method to translate between skill identifiers and stat types. Feel free to replace it if there's a better way
|
||||
switch (skillIdentifier)
|
||||
|
||||
@@ -462,6 +462,9 @@ namespace Barotrauma
|
||||
|
||||
public bool IsAttachmentsLoaded => HairIndex > -1 && BeardIndex > -1 && MoustacheIndex > -1 && FaceAttachmentIndex > -1;
|
||||
|
||||
// talent-relevant values
|
||||
public int MissionsCompletedSinceDeath = 0;
|
||||
|
||||
// Used for creating the data
|
||||
public CharacterInfo(string speciesName, string name = "", string originalName = "", JobPrefab jobPrefab = null, string ragdollFileName = null, int variant = 0, Rand.RandSync randSync = Rand.RandSync.Unsynced, string npcIdentifier = "")
|
||||
{
|
||||
@@ -605,7 +608,10 @@ namespace Barotrauma
|
||||
if (!string.IsNullOrEmpty(personalityName))
|
||||
{
|
||||
personalityTrait = NPCPersonalityTrait.List.Find(p => p.Name == personalityName);
|
||||
}
|
||||
}
|
||||
|
||||
MissionsCompletedSinceDeath = infoElement.GetAttributeInt("missionscompletedsincedeath", 0);
|
||||
|
||||
foreach (XElement subElement in infoElement.Elements())
|
||||
{
|
||||
bool jobCreated = false;
|
||||
@@ -973,16 +979,17 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float prevLevel = Job.GetSkillLevel(skillIdentifier);
|
||||
Job.IncreaseSkillLevel(skillIdentifier, increase);
|
||||
Job.IncreaseSkillLevel(skillIdentifier, increase, Character.HasAbilityFlag(AbilityFlags.GainSkillPastMaximum));
|
||||
|
||||
float newLevel = Job.GetSkillLevel(skillIdentifier);
|
||||
|
||||
if ((int)newLevel > (int)prevLevel)
|
||||
{
|
||||
Character.CheckTalents(AbilityEffectType.OnGainSkillPoint, skillIdentifier);
|
||||
|
||||
foreach (Character character in Character.GetFriendlyCrew(Character))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnAllyGainSkillPoint, (skillIdentifier, Character));
|
||||
var abilityStringCharacter = new AbilityStringCharacter(skillIdentifier, Character);
|
||||
character.CheckTalents(AbilityEffectType.OnAllyGainSkillPoint, abilityStringCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1139,9 +1146,10 @@ namespace Barotrauma
|
||||
new XAttribute("startitemsgiven", StartItemsGiven),
|
||||
new XAttribute("ragdoll", ragdollFileName),
|
||||
new XAttribute("personality", personalityTrait == null ? "" : personalityTrait.Name));
|
||||
|
||||
// TODO: animations?
|
||||
|
||||
charElement.Add(new XAttribute("missionscompletedsincedeath", MissionsCompletedSinceDeath));
|
||||
|
||||
if (Character != null)
|
||||
{
|
||||
if (Character.AnimController.CurrentHull != null)
|
||||
@@ -1158,6 +1166,7 @@ namespace Barotrauma
|
||||
foreach (var savedStat in statValuePair.Value)
|
||||
{
|
||||
if (savedStat.StatValue == 0f) { continue; }
|
||||
if (savedStat.RemoveAfterRound) { continue; }
|
||||
|
||||
savedStatElement.Add(new XElement("savedstatvalue",
|
||||
new XAttribute("stattype", statValuePair.Key.ToString()),
|
||||
@@ -1168,6 +1177,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
charElement.Add(savedStatElement);
|
||||
|
||||
parentElement.Add(charElement);
|
||||
@@ -1496,7 +1507,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetSavedStatValue(string statIdentifier)
|
||||
{
|
||||
savedStatValues.SelectMany(s => s.Value).Where(s => s.StatIdentifier == statIdentifier).ForEach(v => v.StatValue = 0f);
|
||||
@@ -1514,7 +1524,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath)
|
||||
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath, bool removeAfterRound = false, float maxValue = float.MaxValue)
|
||||
{
|
||||
if (!savedStatValues.ContainsKey(statType))
|
||||
{
|
||||
@@ -1523,12 +1533,11 @@ namespace Barotrauma
|
||||
|
||||
if (savedStatValues[statType].FirstOrDefault(s => s.StatIdentifier == statIdentifier) is SavedStatValue savedStat)
|
||||
{
|
||||
savedStat.StatValue += value;
|
||||
savedStat.RemoveOnDeath = removeOnDeath;
|
||||
savedStat.StatValue = MathHelper.Min(savedStat.StatValue + value, maxValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
savedStatValues[statType].Add(new SavedStatValue(statIdentifier, value, removeOnDeath));
|
||||
savedStatValues[statType].Add(new SavedStatValue(statIdentifier, MathHelper.Min(value, maxValue), removeOnDeath, removeAfterRound));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1538,12 +1547,14 @@ namespace Barotrauma
|
||||
public string StatIdentifier { get; set; }
|
||||
public float StatValue { get; set; }
|
||||
public bool RemoveOnDeath { get; set; }
|
||||
public bool RemoveAfterRound { get; set; }
|
||||
|
||||
public SavedStatValue(string statIdentifier, float value, bool removeOnDeath)
|
||||
public SavedStatValue(string statIdentifier, float value, bool removeOnDeath, bool retainAfterRound)
|
||||
{
|
||||
StatValue = value;
|
||||
RemoveOnDeath = removeOnDeath;
|
||||
StatIdentifier = statIdentifier;
|
||||
RemoveAfterRound = retainAfterRound;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-25
@@ -17,6 +17,8 @@ namespace Barotrauma
|
||||
public float PendingAdditionStrength { get; set; }
|
||||
public float AdditionStrength { get; set; }
|
||||
|
||||
private float fluctuationTimer;
|
||||
|
||||
protected float _strength;
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
@@ -56,6 +58,8 @@ namespace Barotrauma
|
||||
|
||||
public readonly Dictionary<AfflictionPrefab.PeriodicEffect, float> PeriodicEffectTimers = new Dictionary<AfflictionPrefab.PeriodicEffect, float>();
|
||||
|
||||
public double AppliedAsSuccessfulTreatmentTime, AppliedAsFailedTreatmentTime;
|
||||
|
||||
/// <summary>
|
||||
/// Which character gave this affliction
|
||||
/// </summary>
|
||||
@@ -123,7 +127,7 @@ namespace Barotrauma
|
||||
float amount = MathHelper.Lerp(
|
||||
currentEffect.MinGrainStrength,
|
||||
currentEffect.MaxGrainStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
|
||||
if (Prefab.GrainBurst > 0 && AdditionStrength > amount)
|
||||
{
|
||||
@@ -138,12 +142,12 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenDistortStrength - currentEffect.MinScreenDistortStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenDistort - currentEffect.MinScreenDistort < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinScreenDistortStrength,
|
||||
currentEffect.MaxScreenDistortStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
currentEffect.MinScreenDistort,
|
||||
currentEffect.MaxScreenDistort,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
public float GetRadialDistortStrength()
|
||||
@@ -151,12 +155,12 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxRadialDistortStrength - currentEffect.MinRadialDistortStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxRadialDistort - currentEffect.MinRadialDistort < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinRadialDistortStrength,
|
||||
currentEffect.MaxRadialDistortStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
currentEffect.MinRadialDistort,
|
||||
currentEffect.MaxRadialDistort,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
public float GetChromaticAberrationStrength()
|
||||
@@ -164,12 +168,12 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxChromaticAberrationStrength - currentEffect.MinChromaticAberrationStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxChromaticAberration - currentEffect.MinChromaticAberration < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinChromaticAberrationStrength,
|
||||
currentEffect.MaxChromaticAberrationStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
currentEffect.MinChromaticAberration,
|
||||
currentEffect.MaxChromaticAberration,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
public float GetScreenBlurStrength()
|
||||
@@ -177,12 +181,18 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenBlurStrength - currentEffect.MinScreenBlurStrength < 0.0f) { return 0.0f; }
|
||||
if (currentEffect.MaxScreenBlur - currentEffect.MinScreenBlur < 0.0f) { return 0.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinScreenBlurStrength,
|
||||
currentEffect.MaxScreenBlurStrength,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength));
|
||||
currentEffect.MinScreenBlur,
|
||||
currentEffect.MaxScreenBlur,
|
||||
(Strength - currentEffect.MinStrength) / (currentEffect.MaxStrength - currentEffect.MinStrength)) * GetScreenEffectFluctuation(currentEffect);
|
||||
}
|
||||
|
||||
private float GetScreenEffectFluctuation(AfflictionPrefab.Effect currentEffect)
|
||||
{
|
||||
if (currentEffect == null || currentEffect.ScreenEffectFluctuationFrequency <= 0.0f) { return 1.0f; }
|
||||
return ((float)Math.Sin(fluctuationTimer * MathHelper.TwoPi) + 1.0f) * 0.5f;
|
||||
}
|
||||
|
||||
public float GetSkillMultiplier()
|
||||
@@ -210,14 +220,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float GetResistance(string afflictionId)
|
||||
public float GetResistance(AfflictionPrefab affliction)
|
||||
{
|
||||
if (Strength < Prefab.ActivationThreshold) { return 0.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 0.0f; }
|
||||
if (currentEffect.MaxResistance - currentEffect.MinResistance <= 0.0f) { return 0.0f; }
|
||||
if (afflictionId != null && afflictionId != currentEffect.ResistanceFor) { return 0.0f; }
|
||||
|
||||
if (!currentEffect.ResistanceFor.Any(r =>
|
||||
r.Equals(affliction.Identifier, StringComparison.OrdinalIgnoreCase) ||
|
||||
r.Equals(affliction.AfflictionType, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinResistance,
|
||||
currentEffect.MaxResistance,
|
||||
@@ -229,8 +242,6 @@ namespace Barotrauma
|
||||
if (Strength < Prefab.ActivationThreshold) { return 1.0f; }
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return 1.0f; }
|
||||
if (currentEffect.MaxSpeedMultiplier - currentEffect.MinSpeedMultiplier <= 0.0f) { return 1.0f; }
|
||||
|
||||
return MathHelper.Lerp(
|
||||
currentEffect.MinSpeedMultiplier,
|
||||
currentEffect.MaxSpeedMultiplier,
|
||||
@@ -282,6 +293,9 @@ namespace Barotrauma
|
||||
AfflictionPrefab.Effect currentEffect = GetActiveEffect();
|
||||
if (currentEffect == null) { return; }
|
||||
|
||||
fluctuationTimer += deltaTime * currentEffect.ScreenEffectFluctuationFrequency;
|
||||
fluctuationTimer %= 1.0f;
|
||||
|
||||
if (currentEffect.StrengthChange < 0) // Reduce diminishing of buffs if boosted
|
||||
{
|
||||
float durationMultiplier = 1 / (1 + (Prefab.IsBuff ? characterHealth.Character.GetStatValue(StatTypes.BuffDurationMultiplier)
|
||||
@@ -290,9 +304,9 @@ namespace Barotrauma
|
||||
_strength += currentEffect.StrengthChange * deltaTime * StrengthDiminishMultiplier * durationMultiplier;
|
||||
|
||||
}
|
||||
else // Reduce strengthening of afflictions if resistant
|
||||
else if (currentEffect.StrengthChange > 0) // Reduce strengthening of afflictions if resistant
|
||||
{
|
||||
_strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab.Identifier));
|
||||
_strength += currentEffect.StrengthChange * deltaTime * (1f - characterHealth.GetResistance(Prefab));
|
||||
}
|
||||
// Don't use the property, because it's virtual and some afflictions like husk overload it for external use.
|
||||
_strength = MathHelper.Clamp(_strength, 0.0f, Prefab.MaxStrength);
|
||||
|
||||
+9
-3
@@ -34,6 +34,10 @@ namespace Barotrauma
|
||||
float threshold = _strength > ActiveThreshold ? ActiveThreshold + 1 : DormantThreshold - 1;
|
||||
float max = Math.Max(threshold, previousValue);
|
||||
_strength = Math.Clamp(value, 0, max);
|
||||
if (previousValue > 0.0f && value <= 0.0f)
|
||||
{
|
||||
DeactivateHusk();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +55,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private float DormantThreshold => Prefab.MaxStrength * 0.5f;
|
||||
private float ActiveThreshold => Prefab.MaxStrength * 0.75f;
|
||||
private float DormantThreshold => (Prefab as AfflictionPrefabHusk)?.DormantThreshold ?? Prefab.MaxStrength * 0.5f;
|
||||
private float ActiveThreshold => (Prefab as AfflictionPrefabHusk)?.ActiveThreshold ?? Prefab.MaxStrength * 0.75f;
|
||||
|
||||
private float TransitionThreshold => (Prefab as AfflictionPrefabHusk)?.TransitionThreshold ?? Prefab.MaxStrength * 0.75f;
|
||||
|
||||
public AfflictionHusk(AfflictionPrefab prefab, float strength) : base(prefab, strength) { }
|
||||
|
||||
@@ -83,7 +89,7 @@ namespace Barotrauma
|
||||
}
|
||||
State = InfectionState.Transition;
|
||||
}
|
||||
else if (Strength < Prefab.MaxStrength)
|
||||
else if (Strength < TransitionThreshold)
|
||||
{
|
||||
if (State != InfectionState.Active)
|
||||
{
|
||||
|
||||
+27
-10
@@ -97,6 +97,10 @@ namespace Barotrauma
|
||||
CauseSpeechImpediment = element.GetAttributeBool("causespeechimpediment", true);
|
||||
NeedsAir = element.GetAttributeBool("needsair", false);
|
||||
ControlHusk = element.GetAttributeBool("controlhusk", false);
|
||||
|
||||
DormantThreshold = element.GetAttributeFloat("dormantthreshold", MaxStrength * 0.5f);
|
||||
ActiveThreshold = element.GetAttributeFloat("activethreshold", MaxStrength * 0.75f);
|
||||
TransitionThreshold = element.GetAttributeFloat("transitionthreshold", MaxStrength);
|
||||
}
|
||||
|
||||
// Use any of these to define which limb the appendage is attached to.
|
||||
@@ -105,6 +109,8 @@ namespace Barotrauma
|
||||
public readonly string AttachLimbName;
|
||||
public readonly LimbType AttachLimbType;
|
||||
|
||||
public float ActiveThreshold, DormantThreshold, TransitionThreshold;
|
||||
|
||||
public readonly string HuskedSpeciesName;
|
||||
public readonly string[] TargetSpecies;
|
||||
public const string Tag = "[speciesname]";
|
||||
@@ -141,28 +147,31 @@ namespace Barotrauma
|
||||
public bool MultiplyByMaxVitality { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinScreenBlurStrength { get; private set; }
|
||||
public float MinScreenBlur { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxScreenBlurStrength { get; private set; }
|
||||
public float MaxScreenBlur { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinScreenDistortStrength { get; private set; }
|
||||
public float MinScreenDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxScreenDistortStrength { get; private set; }
|
||||
public float MaxScreenDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinRadialDistortStrength { get; private set; }
|
||||
public float MinRadialDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxRadialDistortStrength { get; private set; }
|
||||
public float MaxRadialDistort { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinChromaticAberrationStrength { get; private set; }
|
||||
public float MinChromaticAberration { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxChromaticAberrationStrength { get; private set; }
|
||||
public float MaxChromaticAberration { get; private set; }
|
||||
|
||||
[Serialize("255,255,255,255", false)]
|
||||
public Color GrainColor { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinGrainStrength { get; private set; }
|
||||
@@ -170,6 +179,9 @@ namespace Barotrauma
|
||||
[Serialize(0.0f, false)]
|
||||
public float MaxGrainStrength { get; private set; }
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float ScreenEffectFluctuationFrequency { get; private set; }
|
||||
|
||||
[Serialize(1.0f, false)]
|
||||
public float MinBuffMultiplier { get; private set; }
|
||||
|
||||
@@ -188,8 +200,11 @@ namespace Barotrauma
|
||||
[Serialize(1.0f, false)]
|
||||
public float MaxSkillMultiplier { get; private set; }
|
||||
|
||||
[Serialize("", false)]
|
||||
public string ResistanceFor { get; private set; }
|
||||
private readonly string[] resistanceFor;
|
||||
public IEnumerable<string> ResistanceFor
|
||||
{
|
||||
get { return resistanceFor; }
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false)]
|
||||
public float MinResistance { get; private set; }
|
||||
@@ -209,6 +224,8 @@ namespace Barotrauma
|
||||
{
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
resistanceFor = element.GetAttributeStringArray("resistancefor", new string[0], convertToLowerInvariant: true);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
|
||||
@@ -266,6 +266,12 @@ namespace Barotrauma
|
||||
private LimbHealth GetMatchingLimbHealth(Limb limb) => limb == null ? null : limbHealths[limb.HealthIndex];
|
||||
private LimbHealth GetMatchingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb, excludeSevered: false));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
|
||||
/// </summary>
|
||||
private IEnumerable<Affliction> GetMatchingAfflictions(LimbHealth limb)
|
||||
=> limb.Afflictions.Union(afflictions.Where(a => GetMatchingLimbHealth(a) == limb));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
|
||||
/// </summary>
|
||||
@@ -426,18 +432,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float GetResistance(string resistanceId)
|
||||
public float GetResistance(AfflictionPrefab affliction)
|
||||
{
|
||||
float resistance = 0.0f;
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
if (!afflictions[i].Prefab.IsBuff) continue;
|
||||
float temp = afflictions[i].GetResistance(resistanceId);
|
||||
if (temp > resistance) resistance = temp;
|
||||
resistance += afflictions[i].GetResistance(affliction);
|
||||
}
|
||||
resistance = 1 - ((1 - resistance) * Character.GetAbilityResistance(resistanceId));
|
||||
|
||||
return resistance;
|
||||
return 1 - ((1 - resistance) * Character.GetAbilityResistance(affliction.Identifier));
|
||||
}
|
||||
|
||||
public float GetStatValue(StatTypes statType)
|
||||
@@ -451,7 +453,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private readonly List<Affliction> matchingAfflictions = new List<Affliction>();
|
||||
public void ReduceAffliction(Limb targetLimb, string affliction, float amount)
|
||||
public void ReduceAffliction(Limb targetLimb, string affliction, float amount, ActionType? treatmentAction = null)
|
||||
{
|
||||
matchingAfflictions.Clear();
|
||||
matchingAfflictions.AddRange(afflictions);
|
||||
@@ -499,6 +501,17 @@ namespace Barotrauma
|
||||
{
|
||||
matchingAffliction.Strength -= reduceAmount;
|
||||
amount -= reduceAmount;
|
||||
if (treatmentAction != null)
|
||||
{
|
||||
if (treatmentAction.Value == ActionType.OnUse)
|
||||
{
|
||||
matchingAffliction.AppliedAsSuccessfulTreatmentTime = Timing.TotalTime;
|
||||
}
|
||||
else if (treatmentAction.Value == ActionType.OnFailure)
|
||||
{
|
||||
matchingAffliction.AppliedAsFailedTreatmentTime = Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CalculateVitality();
|
||||
@@ -610,7 +623,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (newAffliction.Prefab == affliction.Prefab)
|
||||
{
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier));
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab));
|
||||
if (allowStacking)
|
||||
{
|
||||
// Add the existing strength
|
||||
@@ -632,7 +645,7 @@ namespace Barotrauma
|
||||
//create a new instance of the affliction to make sure we don't use the same instance for multiple characters
|
||||
//or modify the affliction instance of an Attack or a StatusEffect
|
||||
var copyAffliction = newAffliction.Prefab.Instantiate(
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab.Identifier))),
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab))),
|
||||
newAffliction.Source);
|
||||
limbHealth.Afflictions.Add(copyAffliction);
|
||||
|
||||
@@ -666,7 +679,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (newAffliction.Prefab == affliction.Prefab)
|
||||
{
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab.Identifier));
|
||||
float newStrength = newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(affliction.Prefab));
|
||||
if (allowStacking)
|
||||
{
|
||||
// Add the existing strength
|
||||
@@ -688,7 +701,7 @@ namespace Barotrauma
|
||||
//create a new instance of the affliction to make sure we don't use the same instance for multiple characters
|
||||
//or modify the affliction instance of an Attack or a StatusEffect
|
||||
afflictions.Add(newAffliction.Prefab.Instantiate(
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab.Identifier))),
|
||||
Math.Min(newAffliction.Prefab.MaxStrength, newAffliction.Strength * (100.0f / MaxVitality) * (1f - GetResistance(newAffliction.Prefab))),
|
||||
source: newAffliction.Source));
|
||||
|
||||
Character.HealthUpdateInterval = 0.0f;
|
||||
@@ -700,8 +713,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
partial void UpdateLimbAfflictionOverlays();
|
||||
|
||||
public void Update(float deltaTime)
|
||||
@@ -741,7 +752,7 @@ namespace Barotrauma
|
||||
for (int i = afflictions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var affliction = afflictions[i];
|
||||
if (irremovableAfflictions.Contains(affliction)) continue;
|
||||
if (irremovableAfflictions.Contains(affliction)) { continue; }
|
||||
if (affliction.Strength <= 0.0f)
|
||||
{
|
||||
SteamAchievementManager.OnAfflictionRemoved(affliction, Character);
|
||||
@@ -763,6 +774,10 @@ namespace Barotrauma
|
||||
{
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.SwimmingSpeed));
|
||||
}
|
||||
else
|
||||
{
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.WalkingSpeed));
|
||||
}
|
||||
|
||||
UpdateLimbAfflictionOverlays();
|
||||
|
||||
@@ -786,7 +801,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
OxygenAmount = MathHelper.Clamp(OxygenAmount + deltaTime * (Character.OxygenAvailable < InsufficientOxygenThreshold ? -5.0f : 10.0f), -100.0f, 100.0f);
|
||||
float decreaseSpeed = -5.0f;
|
||||
float increaseSpeed = 10.0f;
|
||||
float oxygenlowResistance = GetResistance(oxygenLowAffliction.Prefab);
|
||||
decreaseSpeed *= (1f - oxygenlowResistance);
|
||||
increaseSpeed *= (1f + oxygenlowResistance);
|
||||
OxygenAmount = MathHelper.Clamp(OxygenAmount + deltaTime * (Character.OxygenAvailable < InsufficientOxygenThreshold ? decreaseSpeed : increaseSpeed), -100.0f, 100.0f);
|
||||
}
|
||||
|
||||
UpdateOxygenProjSpecific(prevOxygen, deltaTime);
|
||||
@@ -807,8 +827,6 @@ namespace Barotrauma
|
||||
Vitality = MaxVitality;
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
float damageResistanceMultiplier = 1f - GetResistance("damage");
|
||||
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
foreach (Affliction affliction in limbHealth.Afflictions)
|
||||
@@ -824,7 +842,6 @@ namespace Barotrauma
|
||||
{
|
||||
vitalityDecrease *= limbHealth.VitalityTypeMultipliers[type];
|
||||
}
|
||||
vitalityDecrease *= damageResistanceMultiplier;
|
||||
Vitality -= vitalityDecrease;
|
||||
affliction.CalculateDamagePerSecond(vitalityDecrease);
|
||||
}
|
||||
@@ -833,7 +850,6 @@ namespace Barotrauma
|
||||
foreach (Affliction affliction in afflictions)
|
||||
{
|
||||
float vitalityDecrease = affliction.GetVitalityDecrease(this);
|
||||
vitalityDecrease *= damageResistanceMultiplier;
|
||||
Vitality -= vitalityDecrease;
|
||||
affliction.CalculateDamagePerSecond(vitalityDecrease);
|
||||
}
|
||||
@@ -951,13 +967,13 @@ 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, float randomization = 0.0f)
|
||||
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, float randomization = 0.0f)
|
||||
{
|
||||
//key = item identifier
|
||||
//float = suitability
|
||||
treatmentSuitability.Clear();
|
||||
float minSuitability = -10, maxSuitability = 10;
|
||||
foreach (Affliction affliction in GetAllAfflictions())
|
||||
foreach (Affliction affliction in getAfflictions(limb))
|
||||
{
|
||||
if (affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
|
||||
foreach (KeyValuePair<string, float> treatment in affliction.Prefab.TreatmentSuitability)
|
||||
@@ -990,6 +1006,18 @@ namespace Barotrauma
|
||||
treatmentSuitability[treatment] += Rand.Range(-100.0f, 100.0f) * randomization;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<Affliction> getAfflictions(Limb limb)
|
||||
{
|
||||
if (limb == null)
|
||||
{
|
||||
return GetAllAfflictions();
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetMatchingAfflictions(GetMatchingLimbHealth(limb));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Affliction> activeAfflictions = new List<Affliction>();
|
||||
|
||||
@@ -89,11 +89,11 @@ namespace Barotrauma
|
||||
return (skill == null) ? 0.0f : skill.Level;
|
||||
}
|
||||
|
||||
public void IncreaseSkillLevel(string skillIdentifier, float increase)
|
||||
public void IncreaseSkillLevel(string skillIdentifier, float increase, bool increasePastMax)
|
||||
{
|
||||
if (skills.TryGetValue(skillIdentifier, out Skill skill))
|
||||
{
|
||||
skill.Level += increase;
|
||||
skill.IncreaseSkill(increase, increasePastMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -7,11 +7,18 @@ namespace Barotrauma
|
||||
private float level;
|
||||
|
||||
public string Identifier { get; }
|
||||
|
||||
public const float MaximumSkill = 100.0f;
|
||||
|
||||
public float Level
|
||||
{
|
||||
get { return level; }
|
||||
set { level = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
set { level = value; }
|
||||
}
|
||||
|
||||
public void IncreaseSkill(float value, bool increasePastMax)
|
||||
{
|
||||
level = MathHelper.Clamp(level + value, 0.0f, increasePastMax ? float.MaxValue : MaximumSkill);
|
||||
}
|
||||
|
||||
private Sprite icon;
|
||||
|
||||
@@ -868,7 +868,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool UpdateAttack(float deltaTime, Vector2 attackSimPos, IDamageable damageTarget, out AttackResult attackResult, float distance = -1, Limb targetLimb = null)
|
||||
{
|
||||
attackResult = default(AttackResult);
|
||||
attackResult = default;
|
||||
Vector2 simPos = ragdoll.SimplePhysicsEnabled ? character.SimPosition : SimPosition;
|
||||
float dist = distance > -1 ? distance : ConvertUnits.ToDisplayUnits(Vector2.Distance(simPos, attackSimPos));
|
||||
bool wasRunning = attack.IsRunning;
|
||||
@@ -971,7 +971,7 @@ namespace Barotrauma
|
||||
wasHit = damageTarget != null;
|
||||
}
|
||||
|
||||
if (wasHit)
|
||||
if (wasHit || attack.HitDetectionType == HitDetection.None)
|
||||
{
|
||||
if (character == Character.Controlled || GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
@@ -1132,10 +1132,7 @@ namespace Barotrauma
|
||||
if (statusEffect.type != actionType) { continue; }
|
||||
if (statusEffect.type == ActionType.OnDamaged)
|
||||
{
|
||||
if (statusEffect.AllowedAfflictions != null && (character.LastDamage.Afflictions == null || character.LastDamage.Afflictions.None(a => statusEffect.AllowedAfflictions.Contains(a.Prefab.AfflictionType) || statusEffect.AllowedAfflictions.Contains(a.Prefab.Identifier))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!statusEffect.HasRequiredAfflictions(character.LastDamage)) { continue; }
|
||||
if (statusEffect.OnlyPlayerTriggered)
|
||||
{
|
||||
if (character.LastAttacker == null || !character.LastAttacker.IsPlayer)
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
private readonly string itemIdentifier;
|
||||
private readonly string[] tags;
|
||||
private WeaponType weapontype;
|
||||
private readonly WeaponType weapontype;
|
||||
public AbilityConditionAttackData(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
itemIdentifier = conditionElement.GetAttributeString("itemidentifier", "");
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override bool MatchesConditionSpecific(object abilityData)
|
||||
{
|
||||
if (abilityData is AttackData attackData)
|
||||
if (abilityData is AbilityAttackData attackData)
|
||||
{
|
||||
Item item = attackData?.SourceAttack?.SourceItem;
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityData, typeof(AttackData));
|
||||
LogAbilityConditionError(abilityData, typeof(AbilityAttackData));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityData, typeof(AttackData));
|
||||
LogAbilityConditionError(abilityData, typeof(AbilityAttackData));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionHandsomeStranger : AbilityConditionData
|
||||
{
|
||||
string skillIdentifier;
|
||||
|
||||
public AbilityConditionHandsomeStranger(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
skillIdentifier = conditionElement.GetAttributeString("skillidentifier", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific(object abilityData)
|
||||
{
|
||||
if (abilityData is string skillIdentifier)
|
||||
{
|
||||
return this.skillIdentifier == skillIdentifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityData, typeof(string));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionIsAiming : AbilityConditionDataless
|
||||
{
|
||||
private enum WeaponType
|
||||
{
|
||||
Any = 0,
|
||||
Melee = 1,
|
||||
Ranged = 2
|
||||
};
|
||||
|
||||
private WeaponType weapontype;
|
||||
public AbilityConditionIsAiming(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
switch (conditionElement.GetAttributeString("weapontype", ""))
|
||||
{
|
||||
case "melee":
|
||||
weapontype = WeaponType.Melee;
|
||||
break;
|
||||
case "ranged":
|
||||
weapontype = WeaponType.Ranged;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
bool aimingCorrectItem = false;
|
||||
if (character.AnimController is HumanoidAnimController animController)
|
||||
{
|
||||
foreach (Item item in character.HeldItems)
|
||||
{
|
||||
switch (weapontype)
|
||||
{
|
||||
case WeaponType.Melee:
|
||||
aimingCorrectItem |= item.GetComponent<MeleeWeapon>() != null && animController.IsAimingMelee;
|
||||
break;
|
||||
case WeaponType.Ranged:
|
||||
aimingCorrectItem |= item.GetComponent<RangedWeapon>() != null && animController.IsAiming;
|
||||
break;
|
||||
default:
|
||||
aimingCorrectItem |= animController.IsAiming || animController.IsAimingMelee;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aimingCorrectItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -22,10 +22,9 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
item = tempItem.Prefab;
|
||||
}
|
||||
// this and other instances of this type of casting will be refactored
|
||||
else if (abilityData is (ItemPrefab itemPrefab, object _))
|
||||
else if (abilityData is IAbilityItemPrefab abilityItemPrefab)
|
||||
{
|
||||
item = itemPrefab;
|
||||
item = abilityItemPrefab.ItemPrefab;
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
|
||||
+4
-4
@@ -15,17 +15,17 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override bool MatchesConditionSpecific(object abilityData)
|
||||
{
|
||||
if (abilityData is (Affliction affliction, float reduceAmount))
|
||||
if (abilityData is IAbilityAffliction abilityAffliction)
|
||||
{
|
||||
if (allowedTypes.Find(c => c == affliction.Prefab.AfflictionType) == null) { return false; }
|
||||
if (allowedTypes.Find(c => c == abilityAffliction.Affliction.Prefab.AfflictionType) == null) { return false; }
|
||||
|
||||
if (!string.IsNullOrEmpty(identifier) && affliction.Prefab.Identifier != identifier) { return false; }
|
||||
if (!string.IsNullOrEmpty(identifier) && abilityAffliction.Affliction.Prefab.Identifier != identifier) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityData, typeof((Affliction, float)));
|
||||
LogAbilityConditionError(abilityData, typeof(IAbilityAffliction));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionSkill : AbilityConditionData
|
||||
{
|
||||
private readonly string skillIdentifier;
|
||||
|
||||
public AbilityConditionSkill(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
skillIdentifier = conditionElement.GetAttributeString("skillidentifier", "").ToLowerInvariant();
|
||||
}
|
||||
|
||||
private bool MatchesConditionSpecific(string skillIdentifier)
|
||||
{
|
||||
return this.skillIdentifier == skillIdentifier;
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific(object abilityData)
|
||||
{
|
||||
if ((abilityData as string ?? (abilityData as IAbilityString)?.String) is string skillIdentifier)
|
||||
{
|
||||
return MatchesConditionSpecific(skillIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityData, typeof(string));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -1,11 +1,10 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionAboveVitality : AbilityConditionDataless
|
||||
{
|
||||
float vitalityPercentage;
|
||||
private readonly float vitalityPercentage;
|
||||
|
||||
public AbilityConditionAboveVitality(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionCoauthor : AbilityConditionDataless
|
||||
{
|
||||
private readonly string jobIdentifier;
|
||||
|
||||
public AbilityConditionCoauthor(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
jobIdentifier = conditionElement.GetAttributeString("jobidentifier", string.Empty);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
if (character.SelectedCharacter is Character otherCharacter)
|
||||
{
|
||||
if (!otherCharacter.HasJob(jobIdentifier)) { return false; }
|
||||
if (!(character.SelectedBy == otherCharacter)) { return false; }
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -1,8 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionHasPermanentStat : AbilityConditionDataless
|
||||
{
|
||||
private readonly StatTypes statType;
|
||||
private readonly float min;
|
||||
|
||||
public AbilityConditionHasPermanentStat(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
statType = CharacterAbilityGroup.ParseStatType(conditionElement.GetAttributeString("stattype", ""), characterTalent.DebugIdentifier);
|
||||
min = conditionElement.GetAttributeFloat("min", 0f);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
// should consider decoupling this from stat values entirely
|
||||
return character.Info.GetSavedStatValue(statType) >= min;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionHasStatusTag : AbilityConditionDataless
|
||||
{
|
||||
private readonly string tag;
|
||||
|
||||
|
||||
public AbilityConditionHasStatusTag(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
tag = conditionElement.GetAttributeString("tag", "");
|
||||
if (string.IsNullOrEmpty(tag))
|
||||
{
|
||||
DebugConsole.AddWarning($"Error in talent \"{characterTalent.Prefab.OriginalName}\" - tag not defined in AbilityConditionHasStatusTag.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(tag))
|
||||
{
|
||||
return
|
||||
StatusEffect.DurationList.Any(d => d.Targets.Contains(character) && d.Parent.HasTag(tag)) ||
|
||||
DelayedEffect.DelayList.Any(d => d.Targets.Contains(character) && d.Parent.HasTag(tag));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionInFriendlySubmarine : AbilityConditionDataless
|
||||
{
|
||||
public AbilityConditionInFriendlySubmarine(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
return character.Submarine?.TeamID == character.TeamID;
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionInHull : AbilityConditionDataless
|
||||
{
|
||||
public AbilityConditionInHull(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
return character.CurrentHull != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionLevelsBehindHighest : AbilityConditionDataless
|
||||
{
|
||||
private readonly int levelsBehind;
|
||||
public AbilityConditionLevelsBehindHighest(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
levelsBehind = conditionElement.GetAttributeInt("levelsbehind", 0);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
return Character.GetFriendlyCrew(character).Where(c => c.Info != null && (c.Info.GetCurrentLevel() - character.Info.GetCurrentLevel() >= levelsBehind)).Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -24,13 +24,13 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override bool MatchesConditionSpecific(object abilityData)
|
||||
{
|
||||
if (abilityData is (Mission mission, AbilityValue missionAbilityValue))
|
||||
if (abilityData is IAbilityMission abilityMission)
|
||||
{
|
||||
return mission.Prefab.Type == missionType;
|
||||
return abilityMission.Mission.Prefab.Type == missionType;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityData, typeof((Mission, AbilityValue)));
|
||||
LogAbilityConditionError(abilityData, typeof(IAbilityMission));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-6
@@ -1,14 +1,10 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionServerRandom : AbilityConditionDataless
|
||||
{
|
||||
private float randomChance = 0f;
|
||||
private readonly float randomChance = 0f;
|
||||
public override bool AllowClientSimulation => false;
|
||||
|
||||
public AbilityConditionServerRandom(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
interface IAbilityItemPrefab
|
||||
{
|
||||
public ItemPrefab ItemPrefab { get; set; }
|
||||
}
|
||||
|
||||
interface IAbilityValue
|
||||
{
|
||||
public float Value { get; set; }
|
||||
}
|
||||
|
||||
interface IAbilityMission
|
||||
{
|
||||
public Mission Mission { get; set; }
|
||||
}
|
||||
|
||||
interface IAbilityCharacter
|
||||
{
|
||||
public Character Character { get; set; }
|
||||
}
|
||||
|
||||
interface IAbilityString
|
||||
{
|
||||
public string String { get; set; }
|
||||
}
|
||||
|
||||
interface IAbilityAffliction
|
||||
{
|
||||
public Affliction Affliction { get; set; }
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
|
||||
class AbilityValue : IAbilityValue
|
||||
{
|
||||
public AbilityValue(float value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
public float Value { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueItem : IAbilityValue, IAbilityItemPrefab
|
||||
{
|
||||
public AbilityValueItem(float value, ItemPrefab itemPrefab)
|
||||
{
|
||||
Value = value;
|
||||
ItemPrefab = itemPrefab;
|
||||
}
|
||||
public float Value { get; set; }
|
||||
public ItemPrefab ItemPrefab { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueString : IAbilityValue, IAbilityString
|
||||
{
|
||||
public AbilityValueString(float value, string abilityString)
|
||||
{
|
||||
Value = value;
|
||||
String = abilityString;
|
||||
}
|
||||
public float Value { get; set; }
|
||||
public string String { get; set; }
|
||||
}
|
||||
|
||||
class AbilityStringCharacter : IAbilityCharacter, IAbilityString
|
||||
{
|
||||
public AbilityStringCharacter(string abilityString, Character character)
|
||||
{
|
||||
String = abilityString;
|
||||
Character = character;
|
||||
}
|
||||
public Character Character { get; set; }
|
||||
public string String { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueAffliction : IAbilityValue, IAbilityAffliction
|
||||
{
|
||||
public AbilityValueAffliction(float value, Affliction affliction)
|
||||
{
|
||||
Value = value;
|
||||
Affliction = affliction;
|
||||
}
|
||||
public float Value { get; set; }
|
||||
public Affliction Affliction { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueMission : IAbilityValue, IAbilityMission
|
||||
{
|
||||
public AbilityValueMission(float value, Mission mission)
|
||||
{
|
||||
Value = value;
|
||||
Mission = mission;
|
||||
}
|
||||
public float Value { get; set; }
|
||||
public Mission Mission { get; set; }
|
||||
}
|
||||
|
||||
class AbilityAttackData : IAbilityCharacter
|
||||
{
|
||||
public float DamageMultiplier { get; set; } = 1f;
|
||||
public float AddedPenetration { get; set; } = 0f;
|
||||
public List<Affliction> Afflictions { get; set; }
|
||||
public Attack SourceAttack { get; }
|
||||
public Character Character { get; set; }
|
||||
public Character Attacker { get; set; }
|
||||
|
||||
public AbilityAttackData(Attack sourceAttack, Character character)
|
||||
{
|
||||
SourceAttack = sourceAttack;
|
||||
Character = character;
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -12,12 +12,16 @@ namespace Barotrauma.Abilities
|
||||
public CharacterTalent CharacterTalent { get; }
|
||||
public Character Character { get; }
|
||||
|
||||
public virtual bool RequiresAlive => true;
|
||||
public bool RequiresAlive { get; }
|
||||
|
||||
public virtual bool AllowClientSimulation => false;
|
||||
public virtual bool AppliesEffectOnIntervalUpdate => false;
|
||||
|
||||
private const float DefaultEffectTime = 1.0f;
|
||||
|
||||
// currently resets if the character dies. would need to be stored in a dictionary of sorts to maintain through death
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used primarily for StatusEffects. Default to constant outside interval abilities.
|
||||
/// </summary>
|
||||
@@ -28,6 +32,7 @@ namespace Barotrauma.Abilities
|
||||
CharacterAbilityGroup = characterAbilityGroup;
|
||||
CharacterTalent = characterAbilityGroup.CharacterTalent;
|
||||
Character = CharacterTalent.Character;
|
||||
RequiresAlive = abilityElement.GetAttributeBool("requiresalive", true);
|
||||
}
|
||||
|
||||
public bool IsViable()
|
||||
@@ -132,11 +137,5 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
return flagType;
|
||||
}
|
||||
|
||||
public static float DistanceToSquaredDistance(float distance)
|
||||
{
|
||||
return distance * distance;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+33
-4
@@ -10,16 +10,41 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected readonly List<StatusEffect> statusEffects;
|
||||
|
||||
private readonly bool applyToSelected;
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
|
||||
public CharacterAbilityApplyStatusEffects(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
applyToSelected = abilityElement.GetAttributeBool("applytoselected", false);
|
||||
}
|
||||
|
||||
protected void ApplyEffectSpecific(Character targetCharacter)
|
||||
{
|
||||
foreach (var statusEffect in statusEffects)
|
||||
{
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, targetCharacter);
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.UseTarget))
|
||||
{
|
||||
// currently used this to spawn items on the targeted character
|
||||
statusEffect.SetUser(targetCharacter);
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targetCharacter);
|
||||
}
|
||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
targets.Clear();
|
||||
targets.AddRange(statusEffect.GetNearbyTargets(targetCharacter.WorldPosition, targets));
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targets);
|
||||
}
|
||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.This))
|
||||
{
|
||||
statusEffect.SetUser(Character);
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, Character);
|
||||
}
|
||||
else
|
||||
{
|
||||
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, targetCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +55,17 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
if (abilityData is Character targetCharacter)
|
||||
if (applyToSelected && Character.SelectedCharacter is Character selectedCharacter)
|
||||
{
|
||||
ApplyEffectSpecific(selectedCharacter);
|
||||
}
|
||||
else if ((abilityData as Character ?? (abilityData as IAbilityCharacter)?.Character) is Character targetCharacter)
|
||||
{
|
||||
ApplyEffectSpecific(targetCharacter);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
ApplyEffect();
|
||||
ApplyEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityApplyStatusEffectsToAllies : CharacterAbilityApplyStatusEffects
|
||||
{
|
||||
private readonly bool allowSelf;
|
||||
|
||||
public CharacterAbilityApplyStatusEffectsToAllies(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
allowSelf = abilityElement.GetAttributeBool("allowself", true);
|
||||
}
|
||||
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
IEnumerable<Character> chosenCharacters = Character.GetFriendlyCrew(Character).Where(c => allowSelf || c != Character);
|
||||
|
||||
foreach (Character character in chosenCharacters)
|
||||
{
|
||||
ApplyEffectSpecific(character);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityApplyStatusEffectsToAttacker : CharacterAbilityApplyStatusEffects
|
||||
{
|
||||
public CharacterAbilityApplyStatusEffectsToAttacker(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
if ((abilityData as AbilityAttackData)?.Attacker is Character attacker)
|
||||
{
|
||||
ApplyEffectSpecific(attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,6 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
@@ -10,7 +9,7 @@ namespace Barotrauma.Abilities
|
||||
protected float squaredMaxDistance;
|
||||
public CharacterAbilityApplyStatusEffectsToNearestAlly(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
squaredMaxDistance = DistanceToSquaredDistance(abilityElement.GetAttributeFloat("maxdistance", float.MaxValue));
|
||||
squaredMaxDistance = MathF.Pow(abilityElement.GetAttributeFloat("maxdistance", float.MaxValue), 2);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
@@ -20,7 +19,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
foreach (Character crewCharacter in Character.GetFriendlyCrew(Character))
|
||||
{
|
||||
if (crewCharacter != Character && Vector2.DistanceSquared(Character.SimPosition, Character.GetRelativeSimPosition(crewCharacter)) is float tempDistance && tempDistance < closestDistance)
|
||||
if (crewCharacter != Character && Vector2.DistanceSquared(Character.WorldPosition, crewCharacter.WorldPosition) is float tempDistance && tempDistance < closestDistance)
|
||||
{
|
||||
closestCharacter = crewCharacter;
|
||||
closestDistance = tempDistance;
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -16,7 +17,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
public CharacterAbilityApplyStatusEffectsToRandomAlly(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
squaredMaxDistance = DistanceToSquaredDistance(abilityElement.GetAttributeFloat("maxdistance", float.MaxValue));
|
||||
squaredMaxDistance = MathF.Pow(abilityElement.GetAttributeFloat("maxdistance", float.MaxValue), 2);
|
||||
allowDifferentSub = abilityElement.GetAttributeBool("mustbeonsamesub", true);
|
||||
allowSelf = abilityElement.GetAttributeBool("allowself", true);
|
||||
}
|
||||
@@ -26,9 +27,9 @@ namespace Barotrauma.Abilities
|
||||
Character chosenCharacter = null;
|
||||
|
||||
chosenCharacter = Character.GetFriendlyCrew(Character).Where(c =>
|
||||
(allowSelf ||c != Character) &&
|
||||
(allowSelf || c != Character) &&
|
||||
(allowDifferentSub || c.Submarine == Character.Submarine) &&
|
||||
Vector2.DistanceSquared(Character.SimPosition, Character.GetRelativeSimPosition(c)) is float tempDistance &&
|
||||
Vector2.DistanceSquared(Character.WorldPosition, c.WorldPosition) is float tempDistance &&
|
||||
tempDistance < squaredMaxDistance).GetRandom();
|
||||
|
||||
if (chosenCharacter == null) { return; }
|
||||
|
||||
+27
-2
@@ -7,16 +7,41 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
|
||||
private int amount;
|
||||
private readonly int amount;
|
||||
private StatTypes scalingStatType;
|
||||
|
||||
public CharacterAbilityGiveMoney(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
amount = abilityElement.GetAttributeInt("amount", 0);
|
||||
scalingStatType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("scalingstattype", "None"), CharacterTalent.DebugIdentifier);
|
||||
}
|
||||
|
||||
private void ApplyEffectSpecific(Character targetCharacter)
|
||||
{
|
||||
float multiplier = 1f;
|
||||
if (scalingStatType != StatTypes.None)
|
||||
{
|
||||
multiplier = 0 + Character.Info.GetSavedStatValue(scalingStatType);
|
||||
}
|
||||
|
||||
targetCharacter.GiveMoney((int)(multiplier * amount));
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
if ((abilityData as Character ?? (abilityData as IAbilityCharacter)?.Character) is Character targetCharacter)
|
||||
{
|
||||
ApplyEffectSpecific(targetCharacter);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyEffectSpecific(Character);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
Character.GiveMoney(amount);
|
||||
ApplyEffectSpecific(Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -8,8 +8,10 @@ namespace Barotrauma.Abilities
|
||||
private readonly string statIdentifier;
|
||||
private readonly StatTypes statType;
|
||||
private readonly float value;
|
||||
private readonly float maxValue;
|
||||
private readonly bool targetAllies;
|
||||
private readonly bool removeOnDeath;
|
||||
private readonly bool removeAfterRound;
|
||||
//private readonly float maximumValue;
|
||||
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
@@ -19,8 +21,10 @@ namespace Barotrauma.Abilities
|
||||
statIdentifier = abilityElement.GetAttributeString("statidentifier", "").ToLowerInvariant();
|
||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
||||
value = abilityElement.GetAttributeFloat("value", 0f);
|
||||
maxValue = abilityElement.GetAttributeFloat("maxvalue", float.MaxValue);
|
||||
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
|
||||
removeOnDeath = abilityElement.GetAttributeBool("removeondeath", true);
|
||||
removeAfterRound = abilityElement.GetAttributeBool("removeafterround", false);
|
||||
//maximumValue = abilityElement.GetAttributeFloat("maximumvalue", float.MaxValue);
|
||||
}
|
||||
|
||||
@@ -38,11 +42,11 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
if (targetAllies)
|
||||
{
|
||||
Character.GetFriendlyCrew(Character).ForEach(c => c?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath));
|
||||
Character.GetFriendlyCrew(Character).ForEach(c => c?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
Character?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath);
|
||||
Character?.Info.ChangeSavedStatValue(statType, value, statIdentifier, removeOnDeath, removeAfterRound, maxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-4
@@ -4,18 +4,23 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityGiveResistance : CharacterAbility
|
||||
{
|
||||
private string resistanceId;
|
||||
private float resistance;
|
||||
private readonly string resistanceId;
|
||||
private readonly float multiplier;
|
||||
|
||||
public CharacterAbilityGiveResistance(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
resistanceId = abilityElement.GetAttributeString("resistanceid", "");
|
||||
resistance = abilityElement.GetAttributeFloat("resistance", 1f);
|
||||
multiplier = abilityElement.GetAttributeFloat("multiplier", 1f);
|
||||
|
||||
if (string.IsNullOrEmpty(resistanceId))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in CharacterAbilityGiveResistance - resistance identifier not set.");
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeAbility(bool addingFirstTime)
|
||||
{
|
||||
Character.ChangeAbilityResistance(resistanceId, resistance);
|
||||
Character.ChangeAbilityResistance(resistanceId, multiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -4,10 +4,9 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityGiveStat : CharacterAbility
|
||||
{
|
||||
private StatTypes statType;
|
||||
private float value;
|
||||
private readonly StatTypes statType;
|
||||
private readonly float value;
|
||||
|
||||
// this and resistance giving should probably be moved directly to charactertalent attributes, as they don't need to interact with either ability group types
|
||||
public CharacterAbilityGiveStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
||||
|
||||
+12
-3
@@ -7,8 +7,9 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
private readonly List<Affliction> afflictions;
|
||||
|
||||
float addedDamageMultiplier;
|
||||
float addedPenetration;
|
||||
private readonly float addedDamageMultiplier;
|
||||
private readonly float addedPenetration;
|
||||
private readonly bool implode;
|
||||
|
||||
public CharacterAbilityModifyAttackData(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
@@ -18,11 +19,12 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
addedDamageMultiplier = abilityElement.GetAttributeFloat("addeddamagemultiplier", 0f);
|
||||
addedPenetration = abilityElement.GetAttributeFloat("addedpenetration", 0f);
|
||||
implode = abilityElement.GetAttributeBool("implode", false);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
if (abilityData is AttackData attackData)
|
||||
if (abilityData is AbilityAttackData attackData)
|
||||
{
|
||||
if (attackData.Afflictions == null)
|
||||
{
|
||||
@@ -34,6 +36,13 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
attackData.DamageMultiplier += addedDamageMultiplier;
|
||||
attackData.AddedPenetration += addedPenetration;
|
||||
|
||||
if (implode)
|
||||
{
|
||||
// might have issues, as the method used to be private and only used for pressure death
|
||||
attackData.Character?.Implode();
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+7
-2
@@ -4,8 +4,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityModifyResistance : CharacterAbility
|
||||
{
|
||||
private string resistanceId;
|
||||
private float resistance;
|
||||
private readonly string resistanceId;
|
||||
private readonly float resistance;
|
||||
bool lastState;
|
||||
|
||||
// should probably be split to different classes
|
||||
@@ -13,6 +13,11 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
resistanceId = abilityElement.GetAttributeString("resistanceid", "");
|
||||
resistance = abilityElement.GetAttributeFloat("resistance", 1f);
|
||||
|
||||
if (string.IsNullOrEmpty(resistanceId))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in CharacterAbilityModifyResistance - resistance identifier not set.");
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateCharacterAbility(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityModifyStatToSkill : CharacterAbility
|
||||
{
|
||||
private readonly StatTypes statType;
|
||||
private readonly float maxValue;
|
||||
private readonly string skillIdentifier;
|
||||
private readonly bool useAll;
|
||||
private float lastValue = 0f;
|
||||
|
||||
public CharacterAbilityModifyStatToSkill(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
||||
maxValue = abilityElement.GetAttributeFloat("maxvalue", 0f);
|
||||
skillIdentifier = abilityElement.GetAttributeString("skillidentifier", string.Empty);
|
||||
useAll = skillIdentifier == "all";
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
Character.ChangeStat(statType, -lastValue);
|
||||
|
||||
if (conditionsMatched)
|
||||
{
|
||||
float skillTotal = 0f;
|
||||
|
||||
if (useAll && Character.Info?.Job != null)
|
||||
{
|
||||
foreach (Skill skill in Character.Info.Job.Skills)
|
||||
{
|
||||
skillTotal += Character.GetSkillLevel(skill.Identifier);
|
||||
}
|
||||
skillTotal /= Character.Info.Job.Skills.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
skillTotal = Character.GetSkillLevel(skillIdentifier);
|
||||
}
|
||||
|
||||
lastValue = skillTotal / 100f * maxValue;
|
||||
Character.ChangeStat(statType, lastValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastValue = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-26
@@ -5,42 +5,21 @@ namespace Barotrauma.Abilities
|
||||
class CharacterAbilityModifyValue : CharacterAbility
|
||||
{
|
||||
private float addedValue;
|
||||
private float multiplierValue;
|
||||
private float multiplyValue;
|
||||
|
||||
public CharacterAbilityModifyValue(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
|
||||
multiplierValue = abilityElement.GetAttributeFloat("multipliervalue", 1f);
|
||||
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
if (abilityData is AbilityValue abilityValue)
|
||||
if (abilityData is IAbilityValue abilityValue)
|
||||
{
|
||||
ApplyEffectSpecific(abilityValue);
|
||||
abilityValue.Value += addedValue;
|
||||
abilityValue.Value *= multiplyValue;
|
||||
}
|
||||
else if (abilityData is (object _, AbilityValue tupleAbilityValue))
|
||||
{
|
||||
ApplyEffectSpecific(tupleAbilityValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyEffectSpecific(AbilityValue abilityValue)
|
||||
{
|
||||
abilityValue.Value += addedValue;
|
||||
abilityValue.Value *= multiplierValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// this seems like a real silly way to have to pass values by reference into these same interfaces
|
||||
// if more of these are required, maybe there should be an additional set of interfaces to easily pass values by reference instead
|
||||
class AbilityValue
|
||||
{
|
||||
public float Value { get; set; }
|
||||
public AbilityValue(float value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ namespace Barotrauma.Abilities
|
||||
class CharacterAbilityResetPermanentStat : CharacterAbility
|
||||
{
|
||||
private readonly string statIdentifier;
|
||||
public override bool RequiresAlive => false;
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
|
||||
public CharacterAbilityResetPermanentStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityRevive : CharacterAbility
|
||||
{
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
|
||||
public CharacterAbilityRevive(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
}
|
||||
|
||||
private void ApplyEffectSpecific()
|
||||
{
|
||||
Character.Revive();
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
ApplyEffectSpecific();
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
ApplyEffectSpecific();
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityAlienHoarder : CharacterAbility
|
||||
{
|
||||
private readonly float addedDamageMultiplierPerItem;
|
||||
private readonly int maxAmount;
|
||||
private readonly string[] tags;
|
||||
|
||||
public CharacterAbilityAlienHoarder(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
addedDamageMultiplierPerItem = abilityElement.GetAttributeFloat("addeddamagemultiplierperitem", 0f);
|
||||
maxAmount = abilityElement.GetAttributeInt("maxamount", 0);
|
||||
tags = abilityElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
if (abilityData is AbilityAttackData attackData)
|
||||
{
|
||||
float totalAddedDamageMultiplier = 0f;
|
||||
foreach (Item item in Character.Inventory.AllItems)
|
||||
{
|
||||
if (tags.Any(t => item.Prefab.Tags.Any(p => t == p)))
|
||||
{
|
||||
totalAddedDamageMultiplier += addedDamageMultiplierPerItem;
|
||||
}
|
||||
}
|
||||
attackData.DamageMultiplier += addedDamageMultiplierPerItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityDataMismatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -12,9 +12,9 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override void ApplyEffect(object abilityData)
|
||||
{
|
||||
if (abilityData is (string skillIdentifier, Character character) && character != Character)
|
||||
if (abilityData is AbilityStringCharacter abilityStringCharacter && abilityStringCharacter.Character != Character)
|
||||
{
|
||||
character.Info?.IncreaseSkillLevel(skillIdentifier, 1.0f, character.Position + Vector2.UnitY * 175.0f);
|
||||
Character.Info?.IncreaseSkillLevel(abilityStringCharacter.String, 1.0f, abilityStringCharacter.Character.Position + Vector2.UnitY * 175.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityByTheBook : CharacterAbility
|
||||
{
|
||||
private int moneyAmount;
|
||||
private int max;
|
||||
|
||||
public CharacterAbilityByTheBook(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
moneyAmount = abilityElement.GetAttributeInt("moneyamount", 0);
|
||||
max = abilityElement.GetAttributeInt("max", 0);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
IEnumerable<Character> enemyCharacters = Character.CharacterList.Where(c => c.TeamID == CharacterTeamType.None);
|
||||
|
||||
int timesGiven = 0;
|
||||
foreach (Character enemyCharacter in enemyCharacters)
|
||||
{
|
||||
if (!enemyCharacter.IsHuman) { continue; }
|
||||
if (enemyCharacter.Submarine == null || enemyCharacter.Submarine != Submarine.MainSub) { continue; }
|
||||
if (enemyCharacter.IsDead) { continue; }
|
||||
if (!enemyCharacter.LockHands) { continue; }
|
||||
if (timesGiven > max) { continue; }
|
||||
Character.GiveMoney(moneyAmount);
|
||||
timesGiven++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-27
@@ -9,45 +9,22 @@ namespace Barotrauma.Abilities
|
||||
class CharacterAbilityInsurancePolicy : CharacterAbility
|
||||
{
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
public override bool RequiresAlive => false;
|
||||
|
||||
private readonly int moneyPerLevel;
|
||||
private bool hasOccurred = false;
|
||||
private readonly int moneyPerMission;
|
||||
|
||||
private static List<Client> clientsAlreadyUsed = new List<Client>();
|
||||
|
||||
public CharacterAbilityInsurancePolicy(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
moneyPerLevel = abilityElement.GetAttributeInt("moneyperlevel", 0);
|
||||
moneyPerMission = abilityElement.GetAttributeInt("moneypermission", 0);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
if (Character?.Info is CharacterInfo info && !hasOccurred)
|
||||
if (Character?.Info is CharacterInfo info)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
foreach (Client client in GameMain.NetworkMember.ConnectedClients)
|
||||
{
|
||||
if (client.Character == Character && clientsAlreadyUsed.Contains(client)) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
Character.GiveMoney(moneyPerLevel * info.GetCurrentLevel());
|
||||
hasOccurred = true;
|
||||
|
||||
// this is an ugly way to do this, but this effect should not occur more than once per round for a client
|
||||
// this seemed like the simplest way to do it since characters are instantiated from scratch each time
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
foreach (Client client in GameMain.NetworkMember.ConnectedClients)
|
||||
{
|
||||
if (client.Character == Character)
|
||||
{
|
||||
clientsAlreadyUsed.Add(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
Character.GiveMoney(moneyPerMission * info.MissionsCompletedSinceDeath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -5,14 +5,14 @@ namespace Barotrauma.Abilities
|
||||
class CharacterAbilityPsychoClown : CharacterAbility
|
||||
{
|
||||
private StatTypes statType;
|
||||
private float value;
|
||||
private float maxValue;
|
||||
private string afflictionIdentifier;
|
||||
private float lastValue = 0f;
|
||||
|
||||
public CharacterAbilityPsychoClown(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
|
||||
value = abilityElement.GetAttributeFloat("value", 0f);
|
||||
maxValue = abilityElement.GetAttributeFloat("maxvalue", 0f);
|
||||
afflictionIdentifier = abilityElement.GetAttributeString("afflictionidentifier", "");
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Barotrauma.Abilities
|
||||
afflictionStrength = affliction.Strength / affliction.Prefab.MaxStrength;
|
||||
}
|
||||
|
||||
lastValue = afflictionStrength * value;
|
||||
lastValue = afflictionStrength * maxValue;
|
||||
Character.ChangeStat(statType, lastValue);
|
||||
}
|
||||
else
|
||||
|
||||
+2
@@ -8,6 +8,8 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityRegenerateLoot : CharacterAbility
|
||||
{
|
||||
// not maintained through death, so it's possible for players to respawn and re-loot chests
|
||||
// seems like a minor issue for now
|
||||
List<Item> openedContainers = new List<Item>();
|
||||
|
||||
public CharacterAbilityRegenerateLoot(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
|
||||
+4
-4
@@ -10,20 +10,20 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
private readonly List<StatusEffect> statusEffects;
|
||||
private readonly List<StatusEffect> statusEffectsReset;
|
||||
private int maxEnemyCount;
|
||||
private float squaredDistance;
|
||||
private readonly int maxEnemyCount;
|
||||
private readonly float squaredDistance;
|
||||
|
||||
public CharacterAbilityStonewall(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
|
||||
statusEffectsReset = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffectsreset"));
|
||||
maxEnemyCount = abilityElement.GetAttributeInt("maxenemycount", 0);
|
||||
squaredDistance = DistanceToSquaredDistance(abilityElement.GetAttributeFloat("distance", 0));
|
||||
squaredDistance = MathF.Pow(abilityElement.GetAttributeFloat("distance", 0), 2);
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
int numberOfEnemiesInRange = Character.CharacterList.Where(c => !HumanAIController.IsFriendly(Character, c) && !c.IsDead && Vector2.DistanceSquared(Character.SimPosition, Character.GetRelativeSimPosition(c)) < squaredDistance).Count();
|
||||
int numberOfEnemiesInRange = Character.CharacterList.Count(c => !HumanAIController.IsFriendly(Character, c) && !c.IsDead && Vector2.DistanceSquared(Character.WorldPosition, c.WorldPosition) < squaredDistance);
|
||||
|
||||
foreach (var statusEffect in statusEffectsReset)
|
||||
{
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityTandemFire : CharacterAbilityApplyStatusEffectsToNearestAlly
|
||||
{
|
||||
// this should just be its own class, misleading to inherit here
|
||||
private string tag;
|
||||
public CharacterAbilityTandemFire(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
|
||||
+5
-1
@@ -14,6 +14,10 @@ namespace Barotrauma.Abilities
|
||||
// currently only used to turn off simulation if random conditions are in use
|
||||
public bool IsActive { get; private set; } = true;
|
||||
|
||||
protected int maxTriggerCount { get; }
|
||||
protected int timesTriggered = 0;
|
||||
|
||||
|
||||
// add support for OR conditions?
|
||||
protected readonly List<AbilityCondition> abilityConditions = new List<AbilityCondition>();
|
||||
|
||||
@@ -24,7 +28,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
CharacterTalent = characterTalent;
|
||||
Character = CharacterTalent.Character;
|
||||
|
||||
maxTriggerCount = abilityElementGroup.GetAttributeInt("maxtriggercount", int.MaxValue);
|
||||
foreach (XElement subElement in abilityElementGroup.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
private bool IsApplicable(object abilityData)
|
||||
{
|
||||
if (timesTriggered >= maxTriggerCount) { return false; }
|
||||
return abilityConditions.All(c => c.MatchesCondition(abilityData));
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -14,6 +14,7 @@ namespace Barotrauma.Abilities
|
||||
private float effectDelay;
|
||||
private float effectDelayTimer;
|
||||
|
||||
|
||||
public CharacterAbilityGroupInterval(CharacterTalent characterTalent, XElement abilityElementGroup) : base(characterTalent, abilityElementGroup)
|
||||
{
|
||||
// too many overlapping intervals could cause hitching? maybe randomize a little
|
||||
@@ -42,6 +43,7 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
private bool IsApplicable()
|
||||
{
|
||||
if (timesTriggered >= maxTriggerCount) { return false; }
|
||||
return abilityConditions.All(c => c.MatchesCondition());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,7 @@ namespace Barotrauma
|
||||
|
||||
public static AbilityEffectType ParseAbilityEffectType(CharacterTalent characterTalent, string abilityEffectTypeString)
|
||||
{
|
||||
AbilityEffectType abilityEffectType = AbilityEffectType.Undefined;
|
||||
if (!Enum.TryParse(abilityEffectTypeString, true, out abilityEffectType))
|
||||
if (!Enum.TryParse(abilityEffectTypeString, true, out AbilityEffectType abilityEffectType))
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid ability effect type \"" + abilityEffectTypeString + "\" in CharacterTalent (" + characterTalent.DebugIdentifier + ")");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -13,6 +11,12 @@ namespace Barotrauma
|
||||
public ContentPackage ContentPackage { get; private set; }
|
||||
public string FilePath { get; private set; }
|
||||
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
public string Description { get; private set; }
|
||||
|
||||
public readonly Sprite Icon;
|
||||
|
||||
public static readonly PrefabCollection<TalentPrefab> TalentPrefabs = new PrefabCollection<TalentPrefab>();
|
||||
|
||||
public XElement ConfigElement
|
||||
@@ -26,7 +30,51 @@ namespace Barotrauma
|
||||
FilePath = filePath;
|
||||
ConfigElement = element;
|
||||
Identifier = element.GetAttributeString("identifier", "noidentifier");
|
||||
DisplayName = TextManager.Get("talentname." + Identifier, returnNull: true) ?? Identifier;
|
||||
this.CalculatePrefabUIntIdentifier(TalentPrefabs);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "icon":
|
||||
Icon = new Sprite(subElement);
|
||||
break;
|
||||
case "description":
|
||||
string tempDescription = Description;
|
||||
TextManager.ConstructDescription(ref tempDescription, subElement);
|
||||
Description = tempDescription;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Description))
|
||||
{
|
||||
if (element.Attribute("description") != null)
|
||||
{
|
||||
string description = element.GetAttributeString("description", string.Empty);
|
||||
Description = TextManager.Get(description, returnNull: true) ?? description;
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = TextManager.Get("talentdescription." + Identifier, returnNull: true) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (!TextManager.ContainsTag("talentname." + Identifier))
|
||||
{
|
||||
DebugConsole.AddWarning($"Name for the talent \"{Identifier}\" not found in the text files.");
|
||||
}
|
||||
if (string.IsNullOrEmpty(Description))
|
||||
{
|
||||
DebugConsole.AddWarning($"Description for the talent \"{Identifier}\" not configured");
|
||||
}
|
||||
if (Description.Contains('['))
|
||||
{
|
||||
DebugConsole.ThrowError($"Description for the talent \"{Identifier}\" contains brackets - was some variable not replaced correctly? ({Description})");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool disposed = false;
|
||||
@@ -94,9 +142,6 @@ namespace Barotrauma
|
||||
{
|
||||
LoadFromFile(file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Barotrauma
|
||||
|
||||
private static HashSet<string> subtreeTalents = new HashSet<string>();
|
||||
|
||||
private const string PlaceholderTalent = "placeholder";
|
||||
public XElement ConfigElement
|
||||
{
|
||||
get;
|
||||
@@ -41,6 +42,7 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
@@ -118,6 +120,7 @@ namespace Barotrauma
|
||||
|
||||
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; }
|
||||
|
||||
@@ -178,7 +181,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (XElement talentOptionsElement in subTreeElement.GetChildElements("talentoptions"))
|
||||
{
|
||||
TalentOptionStages.Add(new TalentOption(talentOptionsElement));
|
||||
TalentOptionStages.Add(new TalentOption(talentOptionsElement, Identifier));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,32 +189,19 @@ namespace Barotrauma
|
||||
|
||||
class TalentOption
|
||||
{
|
||||
public readonly List<Talent> Talents = new List<Talent>();
|
||||
public readonly List<TalentPrefab> Talents = new List<TalentPrefab>();
|
||||
|
||||
public TalentOption(XElement talentOptionsElement)
|
||||
public TalentOption(XElement talentOptionsElement, string debugIdentifier)
|
||||
{
|
||||
foreach (XElement talentOptionElement in talentOptionsElement.GetChildElements("talentoption"))
|
||||
{
|
||||
Talents.Add(new Talent(talentOptionElement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Talent
|
||||
{
|
||||
public readonly string Identifier;
|
||||
public readonly Sprite Icon;
|
||||
public Talent(XElement talentOptionElement)
|
||||
{
|
||||
Identifier = talentOptionElement.GetAttributeString("identifier", "");
|
||||
foreach (XElement subElement in talentOptionElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
string identifier = talentOptionElement.GetAttributeString("identifier", string.Empty);
|
||||
if (!TalentPrefab.TalentPrefabs.ContainsKey(identifier))
|
||||
{
|
||||
case "icon":
|
||||
Icon = new Sprite(subElement);
|
||||
break;
|
||||
DebugConsole.ThrowError($"Error in talent tree \"{debugIdentifier}\" - could not find a talent with the identifier \"{identifier}\".");
|
||||
return;
|
||||
}
|
||||
Talents.Add(TalentPrefab.TalentPrefabs[identifier]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user