Skip to content

Commit

Permalink
More Trait Functions (PushMarkup & AddArmor) (Simple-Station#1253)
Browse files Browse the repository at this point in the history
# Description

This PR effectively "Reworks" several of the Bionic Traits through use
of new modular TraitFunctions. These being,

**TraitPushDescription**: Ensures that an entity has the new
ExtendDescriptionComponent, then writes to said component.
ExtendDescriptionComponent serves as a new highly modular "One stop
shop" for any system wanting to add text to the shift-click examine
window. It even accepts arguments for text color, font size, and whether
or not a person must be standing within touching distance to "See" the
provided texts. It accepts arbitrarily any number of descriptions.


**TraitAddArmor**: This takes advantage of a new functionality for the
DamageableSystem, whereby entities are able to have more than one
DamageModifierSet. This allows arbitrarily any number of traits to add
as many modifier sets as desired, without fear of any compatibility
issues. These can be both negative and positive, and as Skubman has
pointed out, this can also be used to create negative traits that make a
character more vulnerable to a given damage type!

Additionally, most of the Bionics Traits have been reworked. CyberEyes
has been split into two modules, one for the base implant, and one for
the Flash Protection. Dermal Armor has been reworked using
TraitAddArmor, so that it no longer replaces your original modifier set,
and instead stacks multiplicatively with whatever your original species
modifier set was. Thus, it can now be taken by any species.

# TODO

<details><summary><h1>Media</h1></summary>
<p>

TraitPushDescription

![image](https://github.com/user-attachments/assets/4661671a-6f20-4cb1-9fad-41c36f7ad79e)

TraitAddArmor

![image](https://github.com/user-attachments/assets/bbc823e1-73bf-471d-b5f6-ef8cdf35c746)

</p>
</details>

# Changelog

:cl:
- add: Five new functions for the Trait System, AddArmor,
PushDescription, ModifyMobThresholds, AddSolutionContainer, and
ModifyStamina.
- tweak: CyberEyes Basic System has been split, now Flash Protection is
a separate module.
- add: Dermal Armor no longer replaces your original species damage
resistances. It now stacks multiplicatively with your original
resistances.
- tweak: Dermal Armor can now be taken by any species, not just Humans.
- add: Dermal Armor, and Bionic Arms can now be revealed by a close
examination. Shift click on someone within touching distance will reveal
if they have these "Obvious" cyberware.

---------

Signed-off-by: VMSolidus <[email protected]>
Co-authored-by: Remuchi <[email protected]>
  • Loading branch information
VMSolidus and Remuchi authored Nov 22, 2024
1 parent 411cb21 commit 29d2cb1
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 48 deletions.
137 changes: 137 additions & 0 deletions Content.Server/Traits/TraitSystem.Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
using Content.Server.Language;
using Content.Shared.Mood;
using Content.Server.NPC.Systems;
using Content.Shared.Traits.Assorted.Components;
using Content.Shared.Damage;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Mobs;
using Content.Shared.Damage.Components;

namespace Content.Server.Traits;

Expand Down Expand Up @@ -263,3 +271,132 @@ public override void OnPlayerSpawn(EntityUid uid,
vvm.WritePath(path, value);
}
}

/// Used for writing to an entity's ExtendDescriptionComponent. If one is not present, it will be added!
/// Use this to create traits that add special descriptions for when a character is shift-click examined.
[UsedImplicitly]
public sealed partial class TraitPushDescription : TraitFunction
{
[DataField, AlwaysPushInheritance]
public List<DescriptionExtension> DescriptionExtensions { get; private set; } = new();

public override void OnPlayerSpawn(EntityUid uid,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager)
{
entityManager.EnsureComponent<ExtendDescriptionComponent>(uid, out var descComp);
foreach (var descExtension in DescriptionExtensions)
descComp.DescriptionList.Add(descExtension);
}
}

[UsedImplicitly]
public sealed partial class TraitAddArmor : TraitFunction
{
/// <summary>
/// The list of prototype ID's of DamageModifierSets to be added to the enumerable damage modifiers of an entity.
/// </summary>
/// <remarks>
/// Dear Maintainer, I'm well aware that validating protoIds is a thing. Unfortunately, this is for a legacy system that doesn't have validated prototypes.
/// And refactoring the entire DamageableSystem is way the hell outside of the scope of the PR adding this function.
/// {FaridaIsCute.png} - Solidus
/// </remarks>
[DataField, AlwaysPushInheritance]
public List<string> DamageModifierSets { get; private set; } = new();

public override void OnPlayerSpawn(EntityUid uid,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager)
{
entityManager.EnsureComponent<DamageableComponent>(uid, out var damageableComponent);
foreach (var modifierSet in DamageModifierSets)
damageableComponent.DamageModifierSets.Add(modifierSet);
}
}

[UsedImplicitly]
public sealed partial class TraitAddSolutionContainer : TraitFunction
{
[DataField, AlwaysPushInheritance]
public Dictionary<string, SolutionComponent> Solutions { get; private set; } = new();

public override void OnPlayerSpawn(EntityUid uid,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager)
{
var solutionContainer = entityManager.System<SharedSolutionContainerSystem>();

foreach (var (containerKey, solution) in Solutions)
{
var hasSolution = solutionContainer.EnsureSolution(uid, containerKey, out Solution? newSolution);

if (!hasSolution)
return;

newSolution!.AddSolution(solution.Solution, null);
}
}
}

[UsedImplicitly]
public sealed partial class TraitModifyMobThresholds : TraitFunction
{
[DataField, AlwaysPushInheritance]
public int CritThresholdModifier;

[DataField, AlwaysPushInheritance]
public int DeadThresholdModifier;

public override void OnPlayerSpawn(EntityUid uid,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager)
{
if (!entityManager.TryGetComponent<MobThresholdsComponent>(uid, out var threshold))
return;

var thresholdSystem = entityManager.System<MobThresholdSystem>();
if (CritThresholdModifier != 0)
{
var critThreshold = thresholdSystem.GetThresholdForState(uid, MobState.Critical, threshold);
if (critThreshold != 0)
thresholdSystem.SetMobStateThreshold(uid, critThreshold + CritThresholdModifier, MobState.Critical);
}

if (DeadThresholdModifier != 0)
{
var deadThreshold = thresholdSystem.GetThresholdForState(uid, MobState.Dead, threshold);
if (deadThreshold != 0)
thresholdSystem.SetMobStateThreshold(uid, deadThreshold + DeadThresholdModifier, MobState.Dead);
}
}
}

[UsedImplicitly]
public sealed partial class TraitModifyStamina : TraitFunction
{
[DataField, AlwaysPushInheritance]
public float StaminaModifier;

[DataField, AlwaysPushInheritance]
public float DecayModifier;

[DataField, AlwaysPushInheritance]
public float CooldownModifier;

public override void OnPlayerSpawn(EntityUid uid,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager)
{
if (!entityManager.TryGetComponent<StaminaComponent>(uid, out var staminaComponent))
return;

staminaComponent.CritThreshold += StaminaModifier;
staminaComponent.Decay += DecayModifier;
staminaComponent.Cooldown += CooldownModifier;
}
}
6 changes: 6 additions & 0 deletions Content.Shared/Damage/Components/DamageableComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ public sealed partial class DamageableComponent : Component
[DataField("damageModifierSet", customTypeSerializer: typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
public string? DamageModifierSetId;

/// <summary>
/// List of all Modifier Sets stored by this entity. The above single format is a deprecated function used only to support legacy yml.
/// </summary>
[DataField]
public List<string> DamageModifierSets = new();

/// <summary>
/// All the damage information is stored in this <see cref="DamageSpecifier"/>.
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions Content.Shared/Damage/Systems/DamageableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ public void DamageChanged(EntityUid uid, DamageableComponent component, DamageSp
// TODO: We need to add a check to see if the given armor covers the targeted part (if any) to modify or not.
damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet);
}

// From Solidus: If you are reading this, I owe you a more comprehensive refactor of this entire system.
if (damageable.DamageModifierSets.Count > 0)
foreach (var enumerableModifierSet in damageable.DamageModifierSets)
if (_prototypeManager.TryIndex<DamageModifierSetPrototype>(enumerableModifierSet, out var enumerableModifier))
damage = DamageSpecifier.ApplyModifierSet(damage, enumerableModifier);

var ev = new DamageModifyEvent(damage, origin, targetPart);
RaiseLocalEvent(uid.Value, ev);
damage = ev.Damage;
Expand Down
11 changes: 0 additions & 11 deletions Content.Shared/Traits/Assorted/Components/CyberEyesComponent.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Robust.Shared.Serialization;

namespace Content.Shared.Traits.Assorted.Components;

[Serializable, NetSerializable, DataDefinition]
public sealed partial class DescriptionExtension
{
[DataField]
public string Description = "";

[DataField]
public int FontSize = 12;

[DataField]
public string Color = "#ffffff";

[DataField]
public bool RequireDetailRange = true;
}

[RegisterComponent]
public sealed partial class ExtendDescriptionComponent : Component
{
/// <summary>
/// The list of all descriptions to add to an entity when examined at close range.
/// </summary>
[DataField]
public List<DescriptionExtension> DescriptionList = new();
}
21 changes: 0 additions & 21 deletions Content.Shared/Traits/Assorted/Systems/CyberEyesSystem.cs

This file was deleted.

27 changes: 27 additions & 0 deletions Content.Shared/Traits/Assorted/Systems/ExtendDescriptionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Content.Shared.Examine;
using Content.Shared.Traits.Assorted.Components;

namespace Content.Shared.Traits.Assorted.Systems;

public sealed class ExtendDescriptionSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ExtendDescriptionComponent, ExaminedEvent>(OnExamined);
}

private void OnExamined(EntityUid uid, ExtendDescriptionComponent component, ExaminedEvent args)
{
if (component.DescriptionList.Count <= 0)
return;

foreach (var desc in component.DescriptionList)
{
if (!args.IsInDetailsRange && desc.RequireDetailRange)
continue;

args.PushMarkup($"[font size ={desc.FontSize}][color={desc.Color}]{Loc.GetString(desc.Description, ("entity", uid))}[/color][/font]");
}
}
}
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/traits/misc.ftl
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
examine-cybereyes-message = {CAPITALIZE(POSS-ADJ($entity))} eyes shine with a faint inner light.
examine-dermal-armor-message = {CAPITALIZE(POSS-ADJ($entity))} skin seems to be made of a sturdy, yet flexible plastic.
examine-bionic-arm-message = {CAPITALIZE(POSS-ADJ($entity))} limbs emit an ever present faint chirp of servomotors.
11 changes: 8 additions & 3 deletions Resources/Locale/en-US/traits/traits.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ trait-description-AddictionNicotine =
trait-name-AnimalFriend = Animal Friend
trait-description-AnimalFriend =
You have a way with animals. You will never be attacked by animals, unless you attack them first.
trait-name-Liar = Pathological liar
trait-description-Liar = You can hardly bring yourself to tell the truth. Sometimes you lie anyway.
Expand Down Expand Up @@ -409,8 +409,13 @@ trait-description-DermalArmor =
trait-name-CyberEyes = Cyber-Eyes Basic System
trait-description-CyberEyes =
One or more of your eyes have been replaced with a highly modular mechanical ocular implant.
Their most basic functionality is to provide amelioration for weaknesses of the wearer's natural eyes,
but additionally these implants provide protection from bright flashes of light.
Their most basic functionality is to provide amelioration for weaknesses of the wearer's natural eyes.
The functionality of these implants can be extended by a variety of commercially available modules.
trait-name-FlareShielding = Cyber-Eyes Flare Shielding
trait-description-FlareShielding =
Your cybereyes have been fitted with a photochromic lense that automatically darkens in response to intense stimuli.
This provides substantial protection from bright flashes of light, such as those from welding arcs.
trait-name-CyberEyesSecurity = Cyber-Eyes SecHud
trait-description-CyberEyesSecurity =
Expand Down
2 changes: 1 addition & 1 deletion Resources/Prototypes/Traits/disabilities.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@
- Vulpkanin # This trait functions exactly as-is for the Vulpkanin trait.
- Shadowkin
functions:
- !type:TraitAddComponent
- !type:TraitReplaceComponent
components:
- type: Flashable
eyeDamageChance: 0.3
Expand Down
51 changes: 39 additions & 12 deletions Resources/Prototypes/Traits/physical.yml
Original file line number Diff line number Diff line change
Expand Up @@ -450,12 +450,18 @@
inverted: true
jobs:
- Prisoner # Bionics should be "Confiscated" from long term prisoners.
- Gladiator
functions:
- !type:TraitAddComponent
components:
- type: Prying
speedModifier: 1
pryPowered: true
- !type:TraitPushDescription
descriptionExtensions:
- description: examine-bionic-arm-message
fontSize: 12
requireDetailRange: true

- type: trait
id: PlateletFactories
Expand Down Expand Up @@ -489,25 +495,26 @@
- type: trait
id: DermalArmor
category: Physical
points: -9
points: -6
requirements:
- !type:CharacterJobRequirement
inverted: true
jobs:
- Prisoner # Bionics should be "Confiscated" from long term prisoners.
- !type:CharacterSpeciesRequirement
species:
- Human
functions:
- !type:TraitReplaceComponent
components:
- type: Damageable
damageModifierSet: DermalArmor
- !type:TraitAddArmor
damageModifierSets:
- DermalArmor
- !type:TraitPushDescription
descriptionExtensions:
- description: examine-dermal-armor-message
fontSize: 12
requireDetailRange: true

- type: trait
id: CyberEyes
category: Physical
points: -8
points: -4
requirements:
- !type:CharacterJobRequirement
inverted: true
Expand All @@ -520,14 +527,34 @@
- Blindness
- Nearsighted
functions:
- !type:TraitRemoveComponent
- !type:TraitPushDescription
descriptionExtensions:
- description: examine-cybereyes-message
fontSize: 12
requireDetailRange: true
- !type:TraitReplaceComponent
components:
- type: Flashable
- type: Flashable # Effectively, removes any flash-vulnerability species traits.


- type: trait
id: FlareShielding
category: Physical
points: -4
requirements:
- !type:CharacterJobRequirement
inverted: true
jobs:
- Prisoner # Bionics should be "Confiscated" from long term prisoners.
- !type:CharacterTraitRequirement
traits:
- CyberEyes
functions:
- !type:TraitAddComponent
components:
- type: FlashImmunity
- type: EyeProtection
- type: CyberEyes


- type: trait
id: CyberEyesSecurity
Expand Down

0 comments on commit 29d2cb1

Please sign in to comment.