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.")]
333 description:
"The amount of resistance to the afflictions specified by ResistanceFor to apply at this effect's lowest strength.")]
337 description:
"The amount of resistance to the afflictions specified by ResistanceFor to apply at this effect's highest strength.")]
340 [
Serialize(
"",
IsPropertySaveable.No, description:
"Identifier used by AI to determine conversation lines to say when this effect is active.")]
343 [
Serialize(
"",
IsPropertySaveable.No, description:
"Tag that enemy AI may use to target the affected character when this effect is active.")]
347 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.")]
351 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.")]
355 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.")]
359 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.")]
395 private readonly
float Value;
428 var afflictionStatValues =
new Dictionary<StatTypes, AppliedStatValue>();
429 var statusEffects =
new List<StatusEffect>();
430 foreach (var subElement
in element.Elements())
432 switch (subElement.Name.ToString().ToLowerInvariant())
439 if (newStatValue.StatType ==
StatTypes.None || !afflictionStatValues.TryAdd(newStatValue.StatType, newStatValue))
441 DebugConsole.ThrowError($
"Invalid stat value in the affliction \"{parentDebugName}\".", contentPackage: element.ContentPackage);
448 DebugConsole.ThrowError($
"Error in affliction \"{parentDebugName}\" - invalid ability flag type \"{subElement.GetAttributeString("flagtype
", "")}\".",
449 contentPackage: element.ContentPackage);
455 DebugConsole.AddWarning($
"Error in affliction \"{parentDebugName}\" - additional afflictions caused by the affliction should be configured inside status effects.",
456 contentPackage: element.ContentPackage);
475 => MathUtils.InverseLerp(
541 if (!text.IsNullOrEmpty())
547 DebugConsole.ThrowError($
"Error in affliction \"{affliction.Identifier}\" - no text defined for one of the descriptions.",
555 DebugConsole.ThrowError($
"Error in affliction \"{affliction.Identifier}\" - max strength is not larger than min.",
580 public readonly List<StatusEffect>
StatusEffects =
new List<StatusEffect>();
586 foreach (var subElement
in element.Elements())
591 if (element.GetAttribute(
"interval") !=
null)
593 MinInterval = MaxInterval = Math.Max(element.GetAttributeFloat(
"interval", 1.0f), 1.0f);
598 MaxInterval = Math.Max(element.GetAttributeFloat(nameof(MaxInterval), 1.0f),
MinInterval);
830 private readonly List<Effect> effects =
new List<Effect>();
835 private readonly List<PeriodicEffect> periodicEffects =
new List<PeriodicEffect>();
837 public IEnumerable<Effect>
Effects => effects;
841 private readonly ConstructorInfo constructor;
858 } =
new Dictionary<Identifier, float>().ToImmutableDictionary();
867 configElement = element;
871 Name = TextManager.Get($
"AfflictionName.{TranslationIdentifier}");
873 if (!
string.IsNullOrEmpty(fallbackName))
877 defaultDescription = TextManager.Get($
"AfflictionDescription.{TranslationIdentifier}");
879 if (!
string.IsNullOrEmpty(fallbackDescription))
881 defaultDescription = defaultDescription.Fallback(fallbackDescription);
910 DebugConsole.ThrowErrorLocalized(
"Error in affliction prefab " +
Name +
" - limb type \"" + indicatorLimbName +
"\" not found.");
932 TextManager.Get($
"AfflictionCauseOfDeath.{TranslationIdentifier}")
935 SelfCauseOfDeathDescription =
936 TextManager.Get($
"AfflictionCauseOfDeathSelf.{TranslationIdentifier}")
953 List<Description> descriptions =
new List<Description>();
954 foreach (var subElement
in element.Elements())
956 switch (subElement.Name.ToString().ToLowerInvariant())
961 case "afflictionoverlay":
965 DebugConsole.ThrowError($
"Error in affliction \"{Identifier}\" - stat values should be configured inside the affliction's effects.",
966 contentPackage: element.ContentPackage);
969 case "periodiceffect":
972 descriptions.Add(
new Description(subElement,
this));
975 DebugConsole.AddWarning($
"Unrecognized element in affliction \"{Identifier}\" ({subElement.Name})",
976 contentPackage: element.ContentPackage);
982 constructor = type.GetConstructor(
new[] { typeof(
AfflictionPrefab), typeof(
float) });
985 private void RefreshTreatmentSuitabilities()
987 var newTreatmentSuitabilities =
new Dictionary<Identifier, float>();
991 float suitability = itemPrefab.GetTreatmentSuitability(
Identifier) + itemPrefab.GetTreatmentSuitability(
AfflictionType);
992 if (!MathUtils.NearlyEqual(suitability, 0.0f))
994 newTreatmentSuitabilities.TryAdd(itemPrefab.Identifier, suitability);
997 HasTreatments = newTreatmentSuitabilities.Any(kvp => kvp.Value > 0);
1005 if (strength < description.MinStrength || strength > description.MaxStrength) {
continue; }
1015 return description.Text;
1017 return defaultDescription;
1025 foreach (var prefab
in Prefabs)
1027 prefab.RefreshTreatmentSuitabilities();
1028 prefab.LoadEffects();
1038 Prefabs.ForEach(p => p.ClearEffects());
1041 private void LoadEffects()
1044 foreach (var subElement
in configElement.Elements())
1046 switch (subElement.Name.ToString().ToLowerInvariant())
1049 effects.Add(
new Effect(subElement,
Name.
Value));
1051 case "periodiceffect":
1052 periodicEffects.Add(
new PeriodicEffect(subElement,
Name.
Value));
1056 for (
int i = 0; i < effects.Count; i++)
1058 for (
int j = i + 1; j < effects.Count; j++)
1062 if (a.MinStrength < b.MaxStrength && b.MinStrength < a.MaxStrength)
1064 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.",
1071 private void ClearEffects()
1074 periodicEffects.Clear();
1080 foreach (var effect
in effects)
1082 foreach (var statusEffect
in effect.StatusEffects)
1084 foreach (var sound
in statusEffect.Sounds)
1090 foreach (var periodicEffect
in periodicEffects)
1092 foreach (var statusEffect
in periodicEffect.StatusEffects)
1094 foreach (var sound
in statusEffect.Sounds)
1105 return $
"AfflictionPrefab ({Name})";
1110 object instance =
null;
1113 instance = constructor.Invoke(
new object[] {
this, strength });
1115 catch (Exception ex)
1117 DebugConsole.ThrowError(ex.InnerException !=
null ? ex.InnerException.ToString() : ex.ToString());
1120 affliction.
Source = source;
1126 foreach (
Effect effect
in effects)
1128 if (currentStrength > effect.MinStrength && currentStrength <= effect.MaxStrength)
1135 Effect strongestEffect =
null;
1136 float largestStrength = currentStrength;
1137 foreach (
Effect effect
in effects)
1139 if (currentStrength > effect.MaxStrength &&
1140 (strongestEffect ==
null || effect.MaxStrength > largestStrength))
1142 strongestEffect = effect;
1146 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...
float MaxAfflictionOverlayAlphaMultiplier
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
LocalizedString GetDescription(float strength, Description.TargetType targetType)
static readonly Identifier PoisonType
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