Make the Bible protect BibleUsers when held (#4726)

* Add HandHeldArmor

* Buff Bible

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix test fail maybe?

* Forgot one

* Adjusting armor

Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>

* Removed Unnecessary Comments

Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>

---------

Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Sir Warock 2025-11-28 20:03:43 +01:00 committed by GitHub
parent 6baef896e9
commit 841bf3f5a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 246 additions and 1 deletions

View File

@ -1,6 +1,8 @@
using Content.Shared.Armor; // DeltaV - Addition of HandHeldArmor
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Camera; using Content.Shared.Camera;
using Content.Shared.Cuffs; using Content.Shared.Cuffs;
using Content.Shared.Damage; // DeltaV End - Addition of HandHeldArmor
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.Projectiles; using Content.Shared.Projectiles;
@ -16,6 +18,10 @@ public abstract partial class SharedHandsSystem
SubscribeLocalEvent<HandsComponent, GetEyeOffsetRelayedEvent>(RelayEvent); SubscribeLocalEvent<HandsComponent, GetEyeOffsetRelayedEvent>(RelayEvent);
SubscribeLocalEvent<HandsComponent, GetEyePvsScaleRelayedEvent>(RelayEvent); SubscribeLocalEvent<HandsComponent, GetEyePvsScaleRelayedEvent>(RelayEvent);
SubscribeLocalEvent<HandsComponent, RefreshMovementSpeedModifiersEvent>(RelayEvent); SubscribeLocalEvent<HandsComponent, RefreshMovementSpeedModifiersEvent>(RelayEvent);
// DeltaV Start - Addition of HandHeldArmor
SubscribeLocalEvent<HandsComponent, CoefficientQueryEvent>(RelayEvent);
SubscribeLocalEvent<HandsComponent, DamageModifyEvent>(RelayEvent);
// DeltaV End - Addition of HandHeldArmor
// By-ref events. // By-ref events.
SubscribeLocalEvent<HandsComponent, ExtinguishEvent>(RefRelayEvent); SubscribeLocalEvent<HandsComponent, ExtinguishEvent>(RefRelayEvent);

View File

@ -0,0 +1,72 @@
using Content.Shared._DV.Armor.Systems;
using Content.Shared.Damage;
using Content.Shared.Whitelist;
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Armor.Components;
/// <summary>
/// Used for armor that can be held in your hands.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(HandHeldArmorSystem))]
public sealed partial class HandHeldArmorComponent : Component
{
/// <summary>
/// The Entity holding the handheld armor.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? Holder;
/// <summary>
/// The damage reduction
/// </summary>
[DataField(required: true), AutoNetworkedField]
public DamageModifierSet Modifiers;
/// <summary>
/// A multiplier applied to the calculated point value
/// to determine the monetary value of the armor
/// </summary>
[DataField, AutoNetworkedField]
public float PriceMultiplier = 1;
/// <summary>
/// If true, you can examine the armor to see the protection. If false, the verb won't appear.
/// </summary>
[DataField, AutoNetworkedField]
public bool ShowArmorOnExamine = true;
/// <summary>
/// DeltaV - Gets the effective stamina melee damage coefficient, based on the armor's blunt protection.
/// </summary>
[ViewVariables]
public float StaminaMeleeDamageCoefficient => Modifiers.Coefficients.GetValueOrDefault("Blunt", 1.0f);
/// <summary>
/// The required components for the held armor to be active while held.
/// </summary>
/// <example>A Bible only protects the holder if they have the BibleUserComponent.</example>
/// <remarks>No whitelist check when null.</remarks>
[DataField, AutoNetworkedField]
public EntityWhitelist? Whitelist;
/// <summary>
/// The Loc string for the message shown in the armor examination if the user fails the whitelist requirements.
/// </summary>
[DataField, AutoNetworkedField]
public string? WhitelistFailMessage;
/// <summary>
/// Components for the held armor to be inactive while held.
/// </summary>
/// <example>A clown with the clumsy cannot make use of a parrying dagger.</example>
/// <remarks>No blacklist check when null.</remarks>
[DataField, AutoNetworkedField]
public EntityWhitelist? Blacklist;
/// <summary>
/// The Loc string for the message shown in the armor examination if the user fails the blacklist requirements.
/// </summary>
[DataField, AutoNetworkedField]
public string? BlacklistFailMessage;
}

View File

@ -0,0 +1,146 @@
using Content.Shared._DV.Armor.Components;
using Content.Shared.Armor;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Verbs;
using Content.Shared.Whitelist;
using Robust.Shared.Utility;
namespace Content.Shared._DV.Armor.Systems;
/// <summary>
/// This handles logic relating to <see cref="HeldArmorComponent" />
/// </summary>
public sealed class HandHeldArmorSystem : EntitySystem
{
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<HandHeldArmorComponent, GotEquippedHandEvent>(OnHandEquipped);
SubscribeLocalEvent<HandHeldArmorComponent, HeldRelayedEvent<CoefficientQueryEvent>>(OnCoefficientQuery);
SubscribeLocalEvent<HandHeldArmorComponent, HeldRelayedEvent<DamageModifyEvent>>(OnDamageModify);
SubscribeLocalEvent<HandHeldArmorComponent, GetVerbsEvent<ExamineVerb>>(OnHeldArmorVerbExamine);
}
private void OnHandEquipped(Entity<HandHeldArmorComponent> armor, ref GotEquippedHandEvent args)
{
armor.Comp.Holder = args.User; // So we can check the whitelists later. The events actually don't carry over the user.
}
private void OnCoefficientQuery(Entity<HandHeldArmorComponent> armor, ref HeldRelayedEvent<CoefficientQueryEvent> args)
{
if (!armor.Comp.Holder.HasValue
|| _whitelist.IsWhitelistFail(armor.Comp.Whitelist, armor.Comp.Holder.Value) // If they pass the lists, add the coefficients.
|| _whitelist.IsBlacklistPass(armor.Comp.Blacklist, armor.Comp.Holder.Value))
return;
foreach (var armorCoefficient in armor.Comp.Modifiers.Coefficients)
{
args.Args.DamageModifiers.Coefficients[armorCoefficient.Key] =
args.Args.DamageModifiers.Coefficients.TryGetValue(armorCoefficient.Key, out var coefficient)
? coefficient * armorCoefficient.Value
: armorCoefficient.Value;
}
}
private void OnDamageModify(Entity<HandHeldArmorComponent> armor, ref HeldRelayedEvent<DamageModifyEvent> args)
{
if (!armor.Comp.Holder.HasValue
|| _whitelist.IsWhitelistFail(armor.Comp.Whitelist, armor.Comp.Holder.Value) // If they pass the lists, add the coefficients.
|| _whitelist.IsBlacklistPass(armor.Comp.Blacklist, armor.Comp.Holder.Value))
return;
args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, armor.Comp.Modifiers);
}
private void OnHeldArmorVerbExamine(Entity<HandHeldArmorComponent> armor, ref GetVerbsEvent<ExamineVerb> args)
{
if (!args.CanInteract
|| !args.CanAccess
|| !armor.Comp.ShowArmorOnExamine)
return;
FormattedMessage examineMarkup;
// If the user is blacklisted or not whitelisted, show what they need. Otherwise, show resistance values.
if (_whitelist.IsWhitelistPassOrNull(armor.Comp.Whitelist, args.User)
&& _whitelist.IsBlacklistFailOrNull(armor.Comp.Blacklist, args.User))
{
examineMarkup = GetHeldArmorExamine(armor);
var ev = new ArmorExamineEvent(examineMarkup);
RaiseLocalEvent(armor, ref ev);
}
else
examineMarkup = GetHeldArmorFailExamine(armor);
_examine.AddDetailedExamineVerb(args, armor, examineMarkup,
Loc.GetString("armor-examinable-verb-text"), "/Textures/Interface/VerbIcons/dot.svg.192dpi.png",
Loc.GetString("armor-examinable-verb-message"));
}
private FormattedMessage GetHeldArmorExamine(HandHeldArmorComponent armor)
{
var msg = new FormattedMessage();
msg.AddMarkupOrThrow(Loc.GetString("held-armor-examine"));
foreach (var coefficientArmor in armor.Modifiers.Coefficients)
{
msg.PushNewline();
var armorType = Loc.GetString("armor-damage-type-" + coefficientArmor.Key.ToLower());
msg.AddMarkupOrThrow(Loc.GetString("armor-coefficient-value",
("type", armorType),
("value", MathF.Round((1f - coefficientArmor.Value) * 100, 1))
));
}
foreach (var flatArmor in armor.Modifiers.FlatReduction) // DeltaV
{
msg.PushNewline();
var armorType = Loc.GetString("armor-damage-type-" + flatArmor.Key.ToLower());
msg.AddMarkupOrThrow(Loc.GetString("armor-reduction-value",
("type", armorType),
("value", flatArmor.Value)
));
}
if (!MathHelper.CloseTo(armor.StaminaMeleeDamageCoefficient, 1.0f))
{
msg.PushNewline();
var reduction = (1 - armor.StaminaMeleeDamageCoefficient) * 100;
msg.AddMarkupOrThrow(Loc.GetString("armor-stamina-melee-coefficient-value",
("value", MathF.Round(reduction, 1))
));
}
return msg;
}
private FormattedMessage GetHeldArmorFailExamine(HandHeldArmorComponent armor)
{
var msg = new FormattedMessage();
msg.AddMarkupOrThrow(Loc.GetString("held-armor-fail-examine"));
if (armor.WhitelistFailMessage != null)
{
msg.PushNewline();
msg.AddMarkupOrThrow(Loc.GetString("held-armor-whitelist-fail", ("reason", Loc.GetString(armor.WhitelistFailMessage))));
}
if (armor.BlacklistFailMessage != null)
{
msg.PushNewline();
msg.AddMarkupOrThrow(Loc.GetString("held-armor-blacklist-fail", ("reason", Loc.GetString(armor.BlacklistFailMessage))));
}
return msg;
}
}

View File

@ -1,3 +1,11 @@
armor-stamina-projectile-coefficient-value = - [color=yellow]Stamina projectile[/color] damage reduced by [color=lightblue]{$value}%[/color]. armor-stamina-projectile-coefficient-value = - [color=yellow]Stamina projectile[/color] damage reduced by [color=lightblue]{$value}%[/color].
armor-stamina-melee-coefficient-value = - [color=yellow]Stamina melee[/color] damage reduced by [color=lightblue]{$value}%[/color]. armor-stamina-melee-coefficient-value = - [color=yellow]Stamina melee[/color] damage reduced by [color=lightblue]{$value}%[/color].
armor-damage-type-shadow = Shadow armor-damage-type-shadow = Shadow
held-armor-examine = [color=cyan]Holding[/color] it provides the following protection:
held-armor-fail-examine = You do not pass the following requirements to profit from its protection:
held-armor-whitelist-fail = You are not [color=yellow]{$reason}[/color].
held-armor-blacklist-fail = You are [color=red]{$reason}[/color].
held-armor-bible-user-fail = faithful

View File

@ -58,8 +58,21 @@
- Book - Book
- type: StealTarget - type: StealTarget
stealGroup: Bible stealGroup: Bible
- type: CollisionWake # DeltaV # Begin DeltaV Additions
- type: CollisionWake
enabled: false enabled: false
- type: HandHeldArmor # Holding the Bible protects you
modifiers:
coefficients:
Cold: 0.7
Asphyxiation: 0.7
Holy: 0.3
Shadow: 0.3
whitelist:
components:
- BibleUser # Only if you're the Chaplain or Mystagogue
whitelistFailMessage: held-armor-bible-user-fail
# End DeltaV Additions
- type: entity - type: entity
parent: [Bible, BaseSyndicateContraband] parent: [Bible, BaseSyndicateContraband]