2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
5 using System.Reflection;
8 using System.Collections.Immutable;
29 private readonly
string insufficientSkillAfflictionIdentifier;
45 ReviveChanceMin = MathHelper.Clamp(element.GetAttributeFloat(
"revivechancemin", 0.05f), 0.0f, 1.0f);
49 StabilizationMin = MathHelper.Max(element.GetAttributeFloat(
"stabilizationmin", 0.05f), 0.0f);
52 DamageSkillThreshold = MathHelper.Clamp(element.GetAttributeFloat(
"damageskillthreshold", 40.0f), 0.0f, 100.0f);
53 DamageSkillMultiplier = MathHelper.Clamp(element.GetAttributeFloat(
"damageskillmultiplier", 0.1f), 0.0f, 100.0f);
55 insufficientSkillAfflictionIdentifier = element.GetAttributeString(
"insufficientskillaffliction",
"");
137 DebugConsole.NewMessage($
"No 'huskedspeciesname' defined for the husk affliction ({Identifier}) in {element}", Color.Orange);
144 DebugConsole.NewMessage($
"No 'targets' defined for the husk affliction ({Identifier}) in {element}", Color.Orange);
148 if (attachElement !=
null)
173 DebugConsole.ThrowError($
"Error in \"{Identifier}\": {nameof(DormantThreshold)} is greater than {nameof(ActiveThreshold)} ({DormantThreshold} > {ActiveThreshold})",
178 DebugConsole.ThrowError($
"Error in \"{Identifier}\": {nameof(ActiveThreshold)} is greater than {nameof(TransitionThreshold)} ({ActiveThreshold} > {TransitionThreshold})",
248 [
Serialize(0.0f,
IsPropertySaveable.No, description:
"How much the affliction's strength changes every second while this effect is active.")]
252 "If set to true, MinVitalityDecrease and MaxVitalityDecrease represent a fraction of the affected character's maximum " +
253 "vitality, with 1 meaning 100%, instead of the same amount for all species.")]
290 "The maximum rate of fluctuation to apply to visual effects caused by this affliction effect. " +
291 "Effective fluctuation is proportional to the affliction's current strength.")]
295 "Multiplier for the affliction overlay's opacity at this effect's lowest strength. " +
296 "See the list of elements for more details.")]
300 "Multiplier for the affliction overlay's opacity at this effect's highest strength. " +
301 "See the list of elements for more details.")]
305 "Multiplier for every buff's decay rate at this effect's lowest strength. " +
306 "Only applies to afflictions of class BuffDurationIncrease.")]
310 "Multiplier for every buff's decay rate at this effect's highest strength. " +
311 "Only applies to afflictions of class BuffDurationIncrease.")]
314 [
Serialize(1.0f,
IsPropertySaveable.No, description:
"Multiplier to apply to the affected character's speed at this effect's lowest strength.")]
317 [
Serialize(1.0f,
IsPropertySaveable.No, description:
"Multiplier to apply to the affected character's speed at this effect's highest strength.")]
320 [
Serialize(1.0f,
IsPropertySaveable.No, description:
"Multiplier to apply to all of the affected character's skill levels at this effect's lowest strength.")]
323 [
Serialize(1.0f,
IsPropertySaveable.No, description:
"Multiplier to apply to all of the affected character's skill levels at this effect's highest strength.")]
338 description:
"The amount of resistance to the afflictions specified by ResistanceFor to apply at this effect's lowest strength.")]
342 description:
"The amount of resistance to the afflictions specified by ResistanceFor to apply at this effect's highest strength.")]
345 [
Serialize(
"",
IsPropertySaveable.No, description:
"Identifier used by AI to determine conversation lines to say when this effect is active.")]
348 [
Serialize(
"",
IsPropertySaveable.No, description:
"Tag that enemy AI may use to target the affected character when this effect is active.")]
352 description:
"Color to tint the affected character's face with at this effect's lowest strength. The alpha channel is used to determine how much to tint the character's face.")]
356 description:
"Color to tint the affected character's face with at this effect's highest strength. The alpha channel is used to determine how much to tint the character's face.")]
360 description:
"Color to tint the affected character's entire body with at this effect's lowest strength. The alpha channel is used to determine how much to tint the character.")]
364 description:
"Color to tint the affected character's entire body with at this effect's highest strength. The alpha channel is used to determine how much to tint the character.")]
368 description:
"Range of the \"thermal goggles overlay\" enabled by the affliction.")]
372 description: $
"Color of the \"thermal goggles overlay\" enabled by the affliction. Only has an effect if {nameof(ThermalOverlayRange)} is larger than 0.")]
408 private readonly
float Value;
443 var afflictionStatValues =
new Dictionary<StatTypes, AppliedStatValue>();
444 var statusEffects =
new List<StatusEffect>();
445 foreach (var subElement
in element.Elements())
447 switch (subElement.Name.ToString().ToLowerInvariant())
454 if (newStatValue.StatType ==
StatTypes.None || !afflictionStatValues.TryAdd(newStatValue.StatType, newStatValue))
456 DebugConsole.ThrowError($
"Invalid stat value in the affliction \"{parentDebugName}\".", contentPackage: element.ContentPackage);
463 DebugConsole.ThrowError($
"Error in affliction \"{parentDebugName}\" - invalid ability flag type \"{subElement.GetAttributeString("flagtype
", "")}\".",
464 contentPackage: element.ContentPackage);
470 DebugConsole.AddWarning($
"Error in affliction \"{parentDebugName}\" - additional afflictions caused by the affliction should be configured inside status effects.",
471 contentPackage: element.ContentPackage);
490 => MathUtils.InverseLerp(
556 if (!text.IsNullOrEmpty())
562 DebugConsole.ThrowError($
"Error in affliction \"{affliction.Identifier}\" - no text defined for one of the descriptions.",
570 DebugConsole.ThrowError($
"Error in affliction \"{affliction.Identifier}\" - max strength is not larger than min.",
595 public readonly List<StatusEffect>
StatusEffects =
new List<StatusEffect>();
601 foreach (var subElement
in element.Elements())
606 if (element.GetAttribute(
"interval") !=
null)
608 MinInterval = MaxInterval = Math.Max(element.GetAttributeFloat(
"interval", 1.0f), 1.0f);
613 MaxInterval = Math.Max(element.GetAttributeFloat(nameof(MaxInterval), 1.0f),
MinInterval);
846 private readonly List<Effect> effects =
new List<Effect>();
851 private readonly List<PeriodicEffect> periodicEffects =
new List<PeriodicEffect>();
853 public IEnumerable<Effect>
Effects => effects;
857 private readonly ConstructorInfo constructor;
874 } =
new Dictionary<Identifier, float>().ToImmutableDictionary();
883 configElement = element;
887 Name = TextManager.Get($
"AfflictionName.{TranslationIdentifier}");
889 if (!
string.IsNullOrEmpty(fallbackName))
893 defaultDescription = TextManager.Get($
"AfflictionDescription.{TranslationIdentifier}");
895 if (!
string.IsNullOrEmpty(fallbackDescription))
897 defaultDescription = defaultDescription.Fallback(fallbackDescription);
918 Name = TextManager.Get(nameIdentifier)
919 .
Fallback(TextManager.Get($
"AfflictionName.{nameIdentifier}"))
929 DebugConsole.ThrowErrorLocalized(
"Error in affliction prefab " +
Name +
" - limb type \"" + indicatorLimbName +
"\" not found.");
951 TextManager.Get($
"AfflictionCauseOfDeath.{TranslationIdentifier}")
954 SelfCauseOfDeathDescription =
955 TextManager.Get($
"AfflictionCauseOfDeathSelf.{TranslationIdentifier}")
972 List<Description> descriptions =
new List<Description>();
973 foreach (var subElement
in element.Elements())
975 switch (subElement.Name.ToString().ToLowerInvariant())
980 case "afflictionoverlay":
984 DebugConsole.ThrowError($
"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.",
985 contentPackage: element.ContentPackage);
988 case "periodiceffect":
991 descriptions.Add(
new Description(subElement,
this));
994 DebugConsole.AddWarning($
"Unrecognized element in affliction \"{Identifier}\" ({subElement.Name})",
995 contentPackage: element.ContentPackage);
1001 constructor = type.GetConstructor(
new[] { typeof(
AfflictionPrefab), typeof(
float) });
1004 private void RefreshTreatmentSuitabilities()
1006 var newTreatmentSuitabilities =
new Dictionary<Identifier, float>();
1010 float suitability = itemPrefab.GetTreatmentSuitability(
Identifier) + itemPrefab.GetTreatmentSuitability(
AfflictionType);
1011 if (!MathUtils.NearlyEqual(suitability, 0.0f))
1013 newTreatmentSuitabilities.TryAdd(itemPrefab.Identifier, suitability);
1016 HasTreatments = newTreatmentSuitabilities.Any(kvp => kvp.Value > 0);
1024 if (strength < description.MinStrength || strength > description.MaxStrength) {
continue; }
1034 return description.Text;
1036 return defaultDescription;
1044 foreach (var prefab
in Prefabs)
1046 prefab.RefreshTreatmentSuitabilities();
1047 prefab.LoadEffects();
1057 Prefabs.ForEach(p => p.ClearEffects());
1060 private void LoadEffects()
1063 foreach (var subElement
in configElement.Elements())
1065 switch (subElement.Name.ToString().ToLowerInvariant())
1068 effects.Add(
new Effect(subElement,
Name.
Value));
1070 case "periodiceffect":
1071 periodicEffects.Add(
new PeriodicEffect(subElement,
Name.
Value));
1075 for (
int i = 0; i < effects.Count; i++)
1077 for (
int j = i + 1; j < effects.Count; j++)
1081 if (a.MinStrength < b.MaxStrength && b.MinStrength < a.MaxStrength)
1083 DebugConsole.AddWarning($
"Affliction \"{Identifier}\" contains effects with overlapping strength ranges. Only one effect can be active at a time, meaning one of the effects won't work.",
1090 private void ClearEffects()
1093 periodicEffects.Clear();
1099 foreach (var effect
in effects)
1101 foreach (var statusEffect
in effect.StatusEffects)
1103 foreach (var sound
in statusEffect.Sounds)
1109 foreach (var periodicEffect
in periodicEffects)
1111 foreach (var statusEffect
in periodicEffect.StatusEffects)
1113 foreach (var sound
in statusEffect.Sounds)
1124 return $
"AfflictionPrefab ({Name})";
1129 object instance =
null;
1132 instance = constructor.Invoke(
new object[] {
this, strength });
1134 catch (Exception ex)
1136 DebugConsole.ThrowError(ex.InnerException !=
null ? ex.InnerException.ToString() : ex.ToString());
1139 affliction.
Source = source;
1145 foreach (
Effect effect
in effects)
1147 if (currentStrength > effect.MinStrength && currentStrength <= effect.MaxStrength)
1154 Effect strongestEffect =
null;
1155 float largestStrength = currentStrength;
1156 foreach (
Effect effect
in effects)
1158 if (currentStrength > effect.MaxStrength &&
1159 (strongestEffect ==
null || effect.MaxStrength > largestStrength))
1161 strongestEffect = effect;
1165 return strongestEffect;
Character Source
Which character gave this affliction
The description element can be used to define descriptions for the affliction which are shown under s...
readonly float MaxStrength
Maximum strength required for the description to be shown.
Description(ContentXElement element, AfflictionPrefab affliction)
readonly float MinStrength
Minimum strength required for the description to be shown.
readonly TargetType Target
Who can see the description.
readonly Identifier TextTag
Text tag used to set the text from the localization files.
readonly LocalizedString Text
Raw text for the description.
Effects are the primary way to add functionality to afflictions.
float MaxVitalityDecrease
readonly ImmutableArray< Identifier > BlockTransformation
Prevents AfflictionHusks with the specified identifier(s) from transforming the character into an AI-...
float GetStrengthFactor(Affliction affliction)
Returns 0 if affliction.Strength is MinStrength, 1 if affliction.Strength is MaxStrength
float MaxChromaticAberration
bool MultiplyByMaxVitality
readonly ImmutableArray< StatusEffect > StatusEffects
readonly AbilityFlags AfflictionAbilityFlags
float MinVitalityDecrease
float ScreenEffectFluctuationFrequency
readonly ImmutableDictionary< StatTypes, AppliedStatValue > AfflictionStatValues
StatType that will be applied to the affected character when the effect is active that is proportiona...
float GetStrengthFactor(float strength)
Returns 0 if affliction.Strength is MinStrength, 1 if affliction.Strength is MaxStrength
float MinChromaticAberration
float MinAfflictionOverlayAlphaMultiplier
Effect(ContentXElement element, string parentDebugName)
readonly ImmutableArray< Identifier > ResistanceFor
A list of identifiers of afflictions that the affected character will be resistant to when this effec...
readonly ImmutableArray< LimbType > ResistanceLimbs
List of limb types that the resistance applies to. If empty, the resistance applies to the whole body...
float MaxAfflictionOverlayAlphaMultiplier
float ThermalOverlayRange
Color ThermalOverlayColor
PeriodicEffect applies StatusEffects to the character periodically.
readonly List< StatusEffect > StatusEffects
readonly float MinInterval
PeriodicEffect(ContentXElement element, string parentDebugName)
readonly float MinStrength
AfflictionPrefab is a prefab that defines a type of affliction that can be applied to a character....
readonly LocalizedString CauseOfDeathDescription
Affliction Instantiate(float strength, Character source=null)
IList< PeriodicEffect > PeriodicEffects
static readonly Identifier InvertControlsType
readonly bool AfflictionOverlayAlphaIsLinear
If set to true and the affliction has an AfflictionOverlay element, the overlay's opacity will be str...
static readonly Identifier BleedingType
static readonly Identifier SpaceHerpesType
readonly float BurnOverlayAlpha
Opacity of the burn effect (darker tint) on limbs affected by this affliction. 1 = full strength.
float KarmaChangeOnApplied
How much karma changes when a player applies this affliction to someone (per strength of the afflicti...
readonly bool ResetBetweenRounds
If set to true, this affliction will not persist between rounds.
static AfflictionPrefab OxygenLow
readonly bool IsBuff
If set to true, the game will recognize this affliction as a buff. This means, among other things,...
readonly Identifier AchievementOnReceived
ImmutableHashSet< Identifier > IgnoreTreatmentIfAfflictedBy
Bots will not try to treat the affliction if the character has any of these afflictions
static void ClearAllEffects()
Removes all the effects of the prefab (including the sounds and other assets defined in them)....
readonly Identifier TranslationIdentifier
Can be set to the identifier of another affliction to make this affliction reuse the same name and de...
readonly int BaseHealCost
The minimum cost of healing this affliction at the medical clinic.
static AfflictionPrefab InternalDamage
readonly Identifier AchievementOnRemoved
Steam achievement given when the affliction is removed from the controlled character.
static readonly Identifier DamageType
static void LoadAllEffectsAndTreatmentSuitabilities()
Should be called before each round: loads all StatusEffects and refreshes treatment suitabilities.
ImmutableDictionary< Identifier, float > TreatmentSuitabilities
static readonly Identifier BurnType
readonly float ShowIconToOthersThreshold
How high the strength has to be for the affliction icon to be shown to others with a health scanner o...
void ReloadSoundsIfNeeded()
readonly float HealCostMultiplier
How much each unit of this affliction's strength will add to the cost of healing at the medical clini...
readonly bool HideIconAfterDelay
If set to true, this affliction's icon will be hidden from the HUD after 5 seconds.
IEnumerable< Effect > Effects
static IEnumerable< AfflictionPrefab > List
readonly bool AffectMachines
If set to true, this affliction can affect characters that are marked as machines,...
Identifier[] TargetSpecies
A list of species this affliction is allowed to affect.
readonly Color[] IconColors
A gradient that defines which color to render this affliction's icon with, based on the affliction's ...
readonly LimbType IndicatorLimb
If the affliction doesn't affect individual limbs, this attribute determines where the game will rend...
readonly float WeaponsSkillGain
An arbitrary modifier that affects how much weapons skill is increased when you apply the affliction ...
override string ToString()
readonly bool ShowBarInHealthMenu
If set to false, the health UI will not show the strength of the affliction as a bar under its indica...
static AfflictionPrefab Burn
readonly bool LimbSpecific
If set to true, the affliction affects individual limbs. Otherwise, it affects the whole character.
readonly float MaxStrength
The maximum strength this affliction can have.
static AfflictionPrefab Bleeding
static AfflictionPrefab ImpactDamage
Effect GetActiveEffect(float currentStrength)
float GetTreatmentSuitability(Item item)
static AfflictionPrefab OrganDamage
bool HasTreatments
Can this affliction be treated with some item?
readonly LocalizedString Name
static readonly Identifier ParalysisType
static readonly Identifier StunType
readonly float Duration
The duration of the affliction, in seconds. If set to 0, the affliction does not expire.
static readonly Identifier AlienInfectedType
readonly bool DamageParticles
Should damage particles be emitted when a character receives this affliction? Only relevant if the af...
readonly Identifier AfflictionType
Arbitrary string that is used to identify the type of the affliction.
static readonly PrefabCollection< AfflictionPrefab > Prefabs
static AfflictionPrefab Stun
readonly float ShowInHealthScannerThreshold
How high the strength has to be for the affliction icon to be shown with a health scanner
readonly float TreatmentThreshold
How strong the affliction needs to be before bots attempt to treat it. Also effects when the afflicti...
readonly ImmutableList< Description > Descriptions
static AfflictionPrefab RadiationSickness
readonly float DamageOverlayAlpha
Opacity of the bloody damage overlay on limbs affected by this affliction. 1 = full strength.
readonly float ActivationThreshold
How high the strength has to be for the affliction to take effect
AfflictionPrefab(ContentXElement element, AfflictionsFile file, Type type)
readonly float ShowIconThreshold
How high the strength has to be for the affliction icon to be shown in the UI
static AfflictionPrefab Pressure
readonly bool HealableInMedicalClinic
If set to true, this affliction can be healed at the medical clinic.
readonly Sprite Icon
An icon that’s used in the UI to represent this affliction.
static AfflictionPrefab BiteWounds
readonly float GrainBurst
The strength of the radiation grain effect to apply when the strength of this affliction increases.
static AfflictionPrefab Bloodloss
readonly float MedicalSkillGain
An arbitrary modifier that affects how much medical skill is increased when you apply the affliction ...
static readonly Identifier EMPType
static readonly Identifier DisguisedAsHuskType
LocalizedString GetDescription(float strength, Description.TargetType targetType)
static readonly Identifier PoisonType
static AfflictionPrefab HuskInfection
readonly Sprite AfflictionOverlay
A sprite that covers the affected player's entire screen when this affliction is active....
AfflictionPrefabHusk is a special type of affliction that has added functionality for husk infection.
readonly LimbType AttachLimbType
readonly bool CauseSpeechImpediment
If set to true, affected characters will have their speech impeded once the affliction reaches the do...
readonly bool TransferBuffs
If set to true, all buffs are transferred to the converted character after husk transformation is com...
readonly bool NeedsAir
If set to false, affected characters will no longer require air once the affliction reaches the activ...
readonly float TransformThresholdOnDeath
The minimum strength the affliction must have for the affected character to transform into a husk upo...
readonly string AttachLimbName
readonly int AttachLimbId
readonly bool ControlHusk
If set to true, affected players will retain control of their character after transforming into a hus...
readonly bool SendMessages
If set to true, the affected player will see on-screen messages describing husk infection symptoms an...
readonly float DormantThreshold
The minimum strength at which husk infection will be in the dormant stage. It must be less than or eq...
readonly float TransitionThreshold
The minimum strength at which husk infection will be in its final stage. It must be greater than or e...
AfflictionPrefabHusk(ContentXElement element, AfflictionsFile file, Type type=null)
readonly Identifier HuskedSpeciesName
The species of husk to convert the affected character to once husk infection reaches its final stage.
readonly float ActiveThreshold
The minimum strength at which husk infection will be in the active stage. It must be greater than or ...
readonly float StabilizationMax
readonly float DamageSkillMultiplier
readonly float DamageSkillThreshold
readonly float ReviveChanceMax
static readonly PrefabSelector< CPRSettings > Prefabs
static CPRSettings Active
readonly float ReviveChancePerSkill
CPRSettings(XElement element, AfflictionsFile file)
readonly float StabilizationMin
readonly float ReviveChanceMin
readonly float StabilizationPerSkill
readonly float ReviveChanceExponent
AfflictionPrefab? InsufficientSkillAffliction
static readonly Identifier HumanSpeciesName
string? GetAttributeString(string key, string? def)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
float GetAttributeFloat(string key, float def)
ContentPackage? ContentPackage
Color?[] GetAttributeColorArray(string key, Color[]? def)
ContentXElement? GetChildElement(string name)
bool GetAttributeBool(string key, bool def)
int GetAttributeInt(string key, int def)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
static readonly PrefabCollection< ItemPrefab > Prefabs
float GetTreatmentSuitability(Identifier treatmentIdentifier)
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.
ContentPackage? ContentPackage
readonly Identifier Identifier
Prefab that has a property serves as a deterministic hash of a prefab's identifier....
static void Reload(RoundSound roundSound)
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)
StatusEffects can be used to execute various kinds of effects: modifying the state of some entity in ...
static StatusEffect Load(ContentXElement element, string parentDebugName)
AbilityFlags
AbilityFlags are a set of toggleable flags that can be applied to characters.
StatTypes
StatTypes are used to alter several traits of a character. They are mostly used by talents.
StatType that will be applied to the affected character when the effect is active that is proportiona...
AppliedStatValue(ContentXElement element)
readonly float MinValue
Minimum value to apply
readonly StatTypes StatType
Which StatType to apply
readonly float MaxValue
Minimum value to apply