From 5d89838f39cf3bd9c83f3370d99bd6bf2837e5c5 Mon Sep 17 00:00:00 2001 From: RevengenRat <138193222+Ratyyy@users.noreply.github.com> Date: Wed, 15 Jan 2025 23:15:14 +0200 Subject: [PATCH] Heretic fix (#984) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Описание PR починка еретика и апстрим до губов --- .../Tests/ADT/Heretic/RitualKnowledgeTests.cs | 81 +++++++++++++++++++ .../Abilities/HereticAbilitySystem.Ash.cs | 49 +++++++---- .../Abilities/HereticAbilitySystem.Blade.cs | 22 ++++- .../EntitySystems/MansusGraspSystem.cs | 6 +- .../PathSpecific/AristocratSystem.cs | 3 +- .../Ritual/CustomBehavior.Knowledge.cs | 5 +- .../Heretic/Components/HereticComponent.cs | 2 + .../ADT/Heretic/Heretic.Abilites.cs | 8 +- .../ru-RU/ADT/Heretic/abilities/heretic.ftl | 3 + Resources/Prototypes/ADT/Datasets/tags.yml | 13 +-- .../Entities/Objects/Specific/heretic.yml | 2 +- .../ADT/Heretic/Heretic/heretic_knowledge.yml | 2 + .../Entities/Objects/Devices/geiger.yml | 3 + Resources/Prototypes/GameRules/events.yml | 1 + 14 files changed, 169 insertions(+), 31 deletions(-) create mode 100644 Content.IntegrationTests/Tests/ADT/Heretic/RitualKnowledgeTests.cs diff --git a/Content.IntegrationTests/Tests/ADT/Heretic/RitualKnowledgeTests.cs b/Content.IntegrationTests/Tests/ADT/Heretic/RitualKnowledgeTests.cs new file mode 100644 index 00000000000..7e29de710dc --- /dev/null +++ b/Content.IntegrationTests/Tests/ADT/Heretic/RitualKnowledgeTests.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Server.Heretic.Ritual; +using Content.Shared.Dataset; +using Content.Shared.Tag; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.ADT.Heretic; + +[TestFixture, TestOf(typeof(RitualKnowledgeBehavior))] +public sealed class RitualKnowledgeTests +{ + [Test] + public async Task ValidateEligibleTags() + { + // As far as I can tell, there's no annotation to validate + // a dataset of tag prototype IDs, so we'll have to do it + // in a test fixture. Sad. + + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entMan = server.ResolveDependency(); + var protoMan = server.ResolveDependency(); + + await server.WaitAssertion(() => + { + // Get the eligible tags prototype + var dataset = protoMan.Index(RitualKnowledgeBehavior.EligibleTagsDataset); + + // Validate that every value is a valid tag + Assert.Multiple(() => + { + foreach (var tagId in dataset.Values) + { + Assert.That(protoMan.TryIndex(tagId, out var tagProto), Is.True, $"\"{tagId}\" is not a valid tag prototype ID"); + } + }); + }); + + await pair.CleanReturnAsync(); + } + + [Test] + public async Task ValidateTagsHaveItems() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entMan = server.ResolveDependency(); + var protoMan = server.ResolveDependency(); + var compFactory = server.ResolveDependency(); + + await server.WaitAssertion(() => + { + // Get the eligible tags prototype + var dataset = protoMan.Index(RitualKnowledgeBehavior.EligibleTagsDataset).Values.ToHashSet(); + + // Loop through every entity prototype and assemble a used tags set + var usedTags = new HashSet(); + + // Ensure that every tag is used by a non-abstract entity + foreach (var entProto in protoMan.EnumeratePrototypes()) + { + if (entProto.Abstract) + continue; + + if (entProto.TryGetComponent(out var tags, compFactory)) + { + usedTags.UnionWith(tags.Tags.Select(t => t.Id)); + } + } + + var unusedTags = dataset.Except(usedTags).ToHashSet(); + Assert.That(unusedTags, Is.Empty, $"The following ritual item tags are not used by any obtainable entity prototypes: {string.Join(", ", unusedTags)}"); + }); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Ash.cs b/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Ash.cs index 39c8dfa59f7..bd3fc51cdb8 100644 --- a/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Ash.cs +++ b/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Ash.cs @@ -1,19 +1,22 @@ -using Content.Server.Atmos.Components; +using Content.Shared.Atmos; +using Content.Shared.Damage; using Content.Shared.Heretic; -using Content.Shared.Mobs.Components; using Content.Shared.Mobs; -using Content.Shared.Damage; -using Content.Shared.Atmos; -using Content.Server.Polymorph.Systems; -using Content.Server.Temperature.Components; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; using Content.Shared.Temperature.Components; +using Content.Server.Atmos.Components; using Content.Server.Body.Components; -using Content.Shared.Armor; +using Content.Server.Temperature.Components; +using Content.Shared.Popups; namespace Content.Server.Heretic.Abilities; public sealed partial class HereticAbilitySystem : EntitySystem { + [Dependency] private readonly IEntityManager _entMan = default!; + [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; + private void SubscribeAsh() { SubscribeLocalEvent(OnJaunt); @@ -29,20 +32,38 @@ private void SubscribeAsh() private void OnJaunt(Entity ent, ref EventHereticAshenShift args) { - if (TryUseAbility(ent, args) && TryDoJaunt(ent)) + var damage = args.Damage; + if (damage != null && ent.Comp.CurrentPath == "Ash") + damage *= float.Lerp(1f, 0.6f, ent.Comp.PathStage * 0.1f); + + // If ent will hit their crit threshold, we don't let them jaunt and give them a popup saying so. + if (damage != null && _entMan.TryGetComponent(ent, out var damageableComp) && _entMan.TryGetComponent(ent, out var thresholdsComp) && _mobThresholdSystem.TryGetThresholdForState(ent, MobState.Critical, out var critThreshold, thresholdsComp)) + { + if (damageableComp.Damage.GetTotal() + damage.GetTotal() >= critThreshold) + { + _popup.PopupEntity(Loc.GetString("heretic-ability-fail-lowhealth", ("damage", damage.GetTotal())), ent, PopupType.LargeCaution); + return; + } + } + + if (TryUseAbility(ent, args) && TryDoJaunt(ent, damage)) args.Handled = true; } private void OnJauntGhoul(Entity ent, ref EventHereticAshenShift args) { - if (TryUseAbility(ent, args) && TryDoJaunt(ent)) + if (TryUseAbility(ent, args) && TryDoJaunt(ent, null)) args.Handled = true; } - private bool TryDoJaunt(EntityUid ent) + private bool TryDoJaunt(EntityUid ent, DamageSpecifier? damage) { Spawn("PolymorphAshJauntAnimation", Transform(ent).Coordinates); var urist = _poly.PolymorphEntity(ent, "AshJaunt"); if (urist == null) return false; + + if (damage != null) + _dmg.TryChangeDamage(ent, damage, true, false); + return true; } @@ -65,7 +86,7 @@ private void OnVolcano(Entity ent, ref EventHereticVolcanoBlas if (!_splitball.Spawn(ent, ignoredTargets)) return; - if (ent.Comp.Ascended) // will only work on ash path + if (ent.Comp is { Ascended: true, CurrentPath: "Ash" }) // will only work on ash path _flammable.AdjustFireStacks(ent, 20f, ignite: true); args.Handled = true; @@ -75,7 +96,7 @@ private void OnNWRebirth(Entity ent, ref EventHereticNightwatc if (!TryUseAbility(ent, args)) return; - var power = ent.Comp.CurrentPath == "Ash" ? ent.Comp.PathStage : 2.5f; + var power = ent.Comp.CurrentPath == "Ash" ? ent.Comp.PathStage : 4f; var lookup = _lookup.GetEntitiesInRange(ent, power); foreach (var look in lookup) @@ -98,7 +119,7 @@ private void OnNWRebirth(Entity ent, ref EventHereticNightwatc _dmg.TryChangeDamage(ent, dmgspec, true, false, dmgc); } - if (!flam.OnFire) + if (flam.OnFire) _flammable.AdjustFireStacks(look, power, flam, true); if (TryComp(look, out var mobstat)) @@ -156,4 +177,4 @@ private void OnAscensionAsh(Entity ent, ref HereticAscensionAs // this does NOT protect you against lasers and whatnot. for now. when i figure out THIS STUPID FUCKING LIMB SYSTEM!!! // regards. } -} \ No newline at end of file +} diff --git a/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Blade.cs b/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Blade.cs index ebd53a6e661..7ee6cf416ff 100644 --- a/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Blade.cs +++ b/Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Blade.cs @@ -3,6 +3,8 @@ using Content.Shared.Damage.Components; using Content.Shared.Heretic; using Content.Shared.Slippery; +using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.CombatMode.Pacification; namespace Content.Server.Heretic.Abilities; @@ -10,6 +12,9 @@ public sealed partial class HereticAbilitySystem : EntitySystem { private void SubscribeBlade() { + SubscribeLocalEvent(OnCuttingEdge); + SubscribeLocalEvent(OnShootAttempt); + SubscribeLocalEvent(OnDanceOfTheBrand); SubscribeLocalEvent(OnRealignment); SubscribeLocalEvent(OnChampionStance); @@ -18,10 +23,25 @@ private void SubscribeBlade() SubscribeLocalEvent(OnAscensionBlade); } + private void OnCuttingEdge(Entity ent, ref HereticCuttingEdgeEvent args) + { + ent.Comp.CanShootGuns = false; + } + + private void OnShootAttempt(Entity ent, ref ShotAttemptedEvent args) + { + if (ent.Comp.CanShootGuns == false) + { + _popup.PopupEntity(Loc.GetString("heretic-cant-shoot", ("entity", args.Used)), ent, ent); + args.Cancel(); + } + } + private void OnDanceOfTheBrand(Entity ent, ref HereticDanceOfTheBrandEvent args) { EnsureComp(ent); } + private void OnRealignment(Entity ent, ref EventHereticRealignment args) { if (!TryUseAbility(ent, args)) @@ -44,7 +64,7 @@ private void OnRealignment(Entity ent, ref EventHereticRealign Dirty(ent, stam); } - _statusEffect.TryAddStatusEffect(ent, "Pacified", TimeSpan.FromSeconds(10f), true); + _statusEffect.TryAddStatusEffect(ent, "Pacified", TimeSpan.FromSeconds(10f), true); args.Handled = true; } diff --git a/Content.Server/ADT/Heretic/EntitySystems/MansusGraspSystem.cs b/Content.Server/ADT/Heretic/EntitySystems/MansusGraspSystem.cs index 30592f1b3f3..fe036b91cbd 100644 --- a/Content.Server/ADT/Heretic/EntitySystems/MansusGraspSystem.cs +++ b/Content.Server/ADT/Heretic/EntitySystems/MansusGraspSystem.cs @@ -43,6 +43,7 @@ public sealed partial class MansusGraspSystem : EntitySystem [Dependency] private readonly DamageableSystem _damage = default!; [Dependency] private readonly TemperatureSystem _temperature = default!; [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() { @@ -69,6 +70,7 @@ private void OnAfterInteract(Entity ent, ref AfterInteract if (!TryComp(args.User, out var hereticComp)) { QueueDel(ent); + args.Handled = true; return; } @@ -101,6 +103,7 @@ private void OnAfterInteract(Entity ent, ref AfterInteract hereticComp.MansusGraspActive = false; QueueDel(ent); + args.Handled = true; } private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) @@ -125,6 +128,7 @@ private void OnAfterInteract(Entity ent, ref AfterInteractEvent ar // spawn our rune var rune = Spawn("HereticRuneRitualDrawAnimation", args.ClickLocation); + _transform.AttachToGridOrMap(rune); var dargs = new DoAfterArgs(EntityManager, args.User, 14f, new DrawRitualRuneDoAfterEvent(rune, args.ClickLocation), args.User) { BreakOnDamage = true, @@ -140,7 +144,7 @@ private void OnRitualRuneDoAfter(Entity ent, ref DrawRitualRun QueueDel(ev.RitualRune); if (!ev.Cancelled) - Spawn("HereticRuneRitual", ev.Coords); + _transform.AttachToGridOrMap(Spawn("HereticRuneRitual", ev.Coords)); } public void ApplyGraspEffect(EntityUid performer, EntityUid target, string path) diff --git a/Content.Server/ADT/Heretic/EntitySystems/PathSpecific/AristocratSystem.cs b/Content.Server/ADT/Heretic/EntitySystems/PathSpecific/AristocratSystem.cs index 578d4634b40..10b0d8fc89b 100644 --- a/Content.Server/ADT/Heretic/EntitySystems/PathSpecific/AristocratSystem.cs +++ b/Content.Server/ADT/Heretic/EntitySystems/PathSpecific/AristocratSystem.cs @@ -33,7 +33,8 @@ public override void Update(float frameTime) { base.Update(frameTime); - while (EntityQueryEnumerator().MoveNext(out var uid, out var aristocrat)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var aristocrat)) { if (!uid.IsValid()) continue; diff --git a/Content.Server/ADT/Heretic/Ritual/CustomBehavior.Knowledge.cs b/Content.Server/ADT/Heretic/Ritual/CustomBehavior.Knowledge.cs index c269b3a86b6..fe34fe851fd 100644 --- a/Content.Server/ADT/Heretic/Ritual/CustomBehavior.Knowledge.cs +++ b/Content.Server/ADT/Heretic/Ritual/CustomBehavior.Knowledge.cs @@ -20,6 +20,9 @@ public sealed partial class RitualKnowledgeBehavior : RitualCustomBehavior private EntityLookupSystem _lookup = default!; private HereticSystem _heretic = default!; + [ValidatePrototypeId] + public const string EligibleTagsDataset = "EligibleTags"; + // this is basically a ripoff from hereticritualsystem public override bool Execute(RitualData args, out string? outstr) { @@ -33,7 +36,7 @@ public override bool Execute(RitualData args, out string? outstr) // generate new set of tags if (requiredTags.Count == 0) for (int i = 0; i < 4; i++) - requiredTags.Add(_rand.Pick(_prot.Index("EligibleTags").Values), 1); + requiredTags.Add(_rand.Pick(_prot.Index(EligibleTagsDataset).Values), 1); var lookup = _lookup.GetEntitiesInRange(args.Platform, .75f); var missingList = new List(); diff --git a/Content.Shared/ADT/Heretic/Components/HereticComponent.cs b/Content.Shared/ADT/Heretic/Components/HereticComponent.cs index 6583fbd7104..5c3f7759e3f 100644 --- a/Content.Shared/ADT/Heretic/Components/HereticComponent.cs +++ b/Content.Shared/ADT/Heretic/Components/HereticComponent.cs @@ -57,4 +57,6 @@ public sealed partial class HereticComponent : Component /// Requires wearing focus, codex cicatrix, hood or anything else that allows him to do so. /// [ViewVariables(VVAccess.ReadWrite)] public bool CanCastSpells = false; + + [ViewVariables(VVAccess.ReadWrite)] public bool CanShootGuns = true; } diff --git a/Content.Shared/ADT/Heretic/Heretic.Abilites.cs b/Content.Shared/ADT/Heretic/Heretic.Abilites.cs index 1d3a51f8db8..e92e8af1a15 100644 --- a/Content.Shared/ADT/Heretic/Heretic.Abilites.cs +++ b/Content.Shared/ADT/Heretic/Heretic.Abilites.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Damage; using Content.Shared.DoAfter; using Content.Shared.Inventory; using Robust.Shared.GameStates; @@ -85,7 +86,11 @@ public sealed partial class EventHereticLivingHeart : InstantActionEvent { } // public sealed partial class EventHereticMansusLink : EntityTargetActionEvent { } // ash -public sealed partial class EventHereticAshenShift : InstantActionEvent { } +public sealed partial class EventHereticAshenShift : InstantActionEvent +{ + [DataField] + public DamageSpecifier? Damage; +} public sealed partial class EventHereticVolcanoBlast : InstantActionEvent { } public sealed partial class EventHereticNightwatcherRebirth : InstantActionEvent { } public sealed partial class EventHereticFlames : InstantActionEvent { } @@ -101,6 +106,7 @@ public sealed partial class HereticVoidBlinkEvent : WorldTargetActionEvent { } public sealed partial class HereticVoidPullEvent : InstantActionEvent { } // blade (+ upgrades) +[Serializable, NetSerializable, DataDefinition] public sealed partial class HereticCuttingEdgeEvent : EntityEventArgs { } [Serializable, NetSerializable, DataDefinition] public sealed partial class HereticDanceOfTheBrandEvent : EntityEventArgs { } public sealed partial class EventHereticRealignment : InstantActionEvent { } [Serializable, NetSerializable, DataDefinition] public sealed partial class HereticChampionStanceEvent : EntityEventArgs { } diff --git a/Resources/Locale/ru-RU/ADT/Heretic/abilities/heretic.ftl b/Resources/Locale/ru-RU/ADT/Heretic/abilities/heretic.ftl index 6e8aa9dd6e8..e01e20d0e89 100644 --- a/Resources/Locale/ru-RU/ADT/Heretic/abilities/heretic.ftl +++ b/Resources/Locale/ru-RU/ADT/Heretic/abilities/heretic.ftl @@ -56,3 +56,6 @@ heretic-speech-blind = E'E'S heretic-speech-emp = E'P heretic-speech-shapeshift = SH'PE heretic-speech-link = PI'RC' TH' M'ND + +heretic-cant-shoot = Я не могу использовать {$entity} из-за моей священной приверженности пути клинка. +heretic-ability-fail-lowhealth = Это заклинание наносит {$damage} урона, оно введёт вас в критическое состояние, если его использовать! \ No newline at end of file diff --git a/Resources/Prototypes/ADT/Datasets/tags.yml b/Resources/Prototypes/ADT/Datasets/tags.yml index 64001cfedb7..2ca3ef0b822 100644 --- a/Resources/Prototypes/ADT/Datasets/tags.yml +++ b/Resources/Prototypes/ADT/Datasets/tags.yml @@ -1,21 +1,12 @@ - type: dataset id: EligibleTags values: - - BreathMask - - ClownEmergencyOxygenTank - - EmergencyMedipen - - EmergencyNitrogenTank - - EmergencyOxygenTank - - ExtendedEmergencyOxygenTank - - MedicalPatch - - SecurityBreathMask - - SpaceMedipen - AirAlarm - AirAlarmElectronics - Airlock - AirSensor - Ambrosia - - ApprisalTool + - AppraisalTool - Arrow - ArtifactFragment - Banana @@ -122,4 +113,4 @@ - Trash - Wirecutter - Wrench - - ADTTeaPack \ No newline at end of file + - ADTTeaPack diff --git a/Resources/Prototypes/ADT/Heretic/Entities/Objects/Specific/heretic.yml b/Resources/Prototypes/ADT/Heretic/Entities/Objects/Specific/heretic.yml index 74fc44d9b7c..fa95bdfbc54 100644 --- a/Resources/Prototypes/ADT/Heretic/Entities/Objects/Specific/heretic.yml +++ b/Resources/Prototypes/ADT/Heretic/Entities/Objects/Specific/heretic.yml @@ -105,4 +105,4 @@ Piercing: 15 Structural: 100 soundHit: - path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg \ No newline at end of file + path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg diff --git a/Resources/Prototypes/ADT/Heretic/Heretic/heretic_knowledge.yml b/Resources/Prototypes/ADT/Heretic/Heretic/heretic_knowledge.yml index 8f37aee77af..d6fd59559d7 100644 --- a/Resources/Prototypes/ADT/Heretic/Heretic/heretic_knowledge.yml +++ b/Resources/Prototypes/ADT/Heretic/Heretic/heretic_knowledge.yml @@ -95,12 +95,14 @@ actionPrototypes: - ActionHereticAscension1 - ActionHereticAscension2 + event: !type:HereticAscensionAshEvent ### blade path - type: hereticKnowledge id: CuttingEdge path: Blade stage: 1 + event: !type:HereticCuttingEdgeEvent ritualPrototypes: - BladeBlade diff --git a/Resources/Prototypes/Entities/Objects/Devices/geiger.yml b/Resources/Prototypes/Entities/Objects/Devices/geiger.yml index f8ee24c5c60..a1703474272 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/geiger.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/geiger.yml @@ -34,4 +34,7 @@ - type: PhysicalComposition materialComposition: Plastic: 100 + - type: Tag # goob edit + tags: + - GeigerCounter diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index cd1a8b849c9..08d420d14d4 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -36,6 +36,7 @@ - id: RevenantSpawn - id: SleeperAgents - id: LoneOpsSpawn # ADT tweak - вернул лон опса в список... АНТАГОВ + - id: Pirates # ADT tweak pirates # - id: ZombieOutbreak - type: entity