Skip to content

Commit

Permalink
Heretic fix (#984)
Browse files Browse the repository at this point in the history
## Описание PR
<!-- Что вы изменили в этом пулл реквесте? -->

починка еретика и апстрим до губов
  • Loading branch information
Ratyyy authored Jan 15, 2025
1 parent ccd83e9 commit 5d89838
Show file tree
Hide file tree
Showing 14 changed files with 169 additions and 31 deletions.
81 changes: 81 additions & 0 deletions Content.IntegrationTests/Tests/ADT/Heretic/RitualKnowledgeTests.cs
Original file line number Diff line number Diff line change
@@ -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<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>();

await server.WaitAssertion(() =>
{
// Get the eligible tags prototype
var dataset = protoMan.Index<DatasetPrototype>(RitualKnowledgeBehavior.EligibleTagsDataset);

// Validate that every value is a valid tag
Assert.Multiple(() =>
{
foreach (var tagId in dataset.Values)
{
Assert.That(protoMan.TryIndex<TagPrototype>(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<IEntityManager>();
var protoMan = server.ResolveDependency<IPrototypeManager>();
var compFactory = server.ResolveDependency<IComponentFactory>();

await server.WaitAssertion(() =>
{
// Get the eligible tags prototype
var dataset = protoMan.Index<DatasetPrototype>(RitualKnowledgeBehavior.EligibleTagsDataset).Values.ToHashSet();

// Loop through every entity prototype and assemble a used tags set
var usedTags = new HashSet<string>();

// Ensure that every tag is used by a non-abstract entity
foreach (var entProto in protoMan.EnumeratePrototypes<EntityPrototype>())
{
if (entProto.Abstract)
continue;

if (entProto.TryGetComponent<TagComponent>(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();
}
}
49 changes: 35 additions & 14 deletions Content.Server/ADT/Heretic/Abilities/HereticAbilitySystem.Ash.cs
Original file line number Diff line number Diff line change
@@ -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<HereticComponent, EventHereticAshenShift>(OnJaunt);
Expand All @@ -29,20 +32,38 @@ private void SubscribeAsh()

private void OnJaunt(Entity<HereticComponent> 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<DamageableComponent>(ent, out var damageableComp) && _entMan.TryGetComponent<MobThresholdsComponent>(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<GhoulComponent> 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;
}

Expand All @@ -65,7 +86,7 @@ private void OnVolcano(Entity<HereticComponent> 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;
Expand All @@ -75,7 +96,7 @@ private void OnNWRebirth(Entity<HereticComponent> 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)
Expand All @@ -98,7 +119,7 @@ private void OnNWRebirth(Entity<HereticComponent> ent, ref EventHereticNightwatc
_dmg.TryChangeDamage(ent, dmgspec, true, false, dmgc);
}

if (!flam.OnFire)
if (flam.OnFire)
_flammable.AdjustFireStacks(look, power, flam, true);

if (TryComp<MobStateComponent>(look, out var mobstat))
Expand Down Expand Up @@ -156,4 +177,4 @@ private void OnAscensionAsh(Entity<HereticComponent> ent, ref HereticAscensionAs
// this does NOT protect you against lasers and whatnot. for now. when i figure out THIS STUPID FUCKING LIMB SYSTEM!!!
// regards.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
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;

public sealed partial class HereticAbilitySystem : EntitySystem
{
private void SubscribeBlade()
{
SubscribeLocalEvent<HereticComponent, HereticCuttingEdgeEvent>(OnCuttingEdge);
SubscribeLocalEvent<HereticComponent, ShotAttemptedEvent>(OnShootAttempt);

SubscribeLocalEvent<HereticComponent, HereticDanceOfTheBrandEvent>(OnDanceOfTheBrand);
SubscribeLocalEvent<HereticComponent, EventHereticRealignment>(OnRealignment);
SubscribeLocalEvent<HereticComponent, HereticChampionStanceEvent>(OnChampionStance);
Expand All @@ -18,10 +23,25 @@ private void SubscribeBlade()
SubscribeLocalEvent<HereticComponent, HereticAscensionBladeEvent>(OnAscensionBlade);
}

private void OnCuttingEdge(Entity<HereticComponent> ent, ref HereticCuttingEdgeEvent args)
{
ent.Comp.CanShootGuns = false;
}

private void OnShootAttempt(Entity<HereticComponent> 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<HereticComponent> ent, ref HereticDanceOfTheBrandEvent args)
{
EnsureComp<RiposteeComponent>(ent);
}

private void OnRealignment(Entity<HereticComponent> ent, ref EventHereticRealignment args)
{
if (!TryUseAbility(ent, args))
Expand All @@ -44,7 +64,7 @@ private void OnRealignment(Entity<HereticComponent> ent, ref EventHereticRealign
Dirty(ent, stam);
}

_statusEffect.TryAddStatusEffect(ent, "Pacified", TimeSpan.FromSeconds(10f), true);
_statusEffect.TryAddStatusEffect<PacifiedComponent>(ent, "Pacified", TimeSpan.FromSeconds(10f), true);

args.Handled = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -69,6 +70,7 @@ private void OnAfterInteract(Entity<MansusGraspComponent> ent, ref AfterInteract
if (!TryComp<HereticComponent>(args.User, out var hereticComp))
{
QueueDel(ent);
args.Handled = true;
return;
}

Expand Down Expand Up @@ -101,6 +103,7 @@ private void OnAfterInteract(Entity<MansusGraspComponent> ent, ref AfterInteract

hereticComp.MansusGraspActive = false;
QueueDel(ent);
args.Handled = true;
}

private void OnAfterInteract(Entity<TagComponent> ent, ref AfterInteractEvent args)
Expand All @@ -125,6 +128,7 @@ private void OnAfterInteract(Entity<TagComponent> 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,
Expand All @@ -140,7 +144,7 @@ private void OnRitualRuneDoAfter(Entity<HereticComponent> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public override void Update(float frameTime)
{
base.Update(frameTime);

while (EntityQueryEnumerator<AristocratComponent>().MoveNext(out var uid, out var aristocrat))
var query = EntityQueryEnumerator<AristocratComponent>();
while (query.MoveNext(out var uid, out var aristocrat))
{
if (!uid.IsValid())
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public sealed partial class RitualKnowledgeBehavior : RitualCustomBehavior
private EntityLookupSystem _lookup = default!;
private HereticSystem _heretic = default!;

[ValidatePrototypeId<DatasetPrototype>]
public const string EligibleTagsDataset = "EligibleTags";

// this is basically a ripoff from hereticritualsystem
public override bool Execute(RitualData args, out string? outstr)
{
Expand All @@ -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<DatasetPrototype>("EligibleTags").Values), 1);
requiredTags.Add(_rand.Pick(_prot.Index<DatasetPrototype>(EligibleTagsDataset).Values), 1);

var lookup = _lookup.GetEntitiesInRange(args.Platform, .75f);
var missingList = new List<string>();
Expand Down
2 changes: 2 additions & 0 deletions Content.Shared/ADT/Heretic/Components/HereticComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)] public bool CanCastSpells = false;

[ViewVariables(VVAccess.ReadWrite)] public bool CanShootGuns = true;
}
8 changes: 7 additions & 1 deletion Content.Shared/ADT/Heretic/Heretic.Abilites.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Shared.Actions;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.Inventory;
using Robust.Shared.GameStates;
Expand Down Expand Up @@ -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 { }
Expand All @@ -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 { }
Expand Down
3 changes: 3 additions & 0 deletions Resources/Locale/ru-RU/ADT/Heretic/abilities/heretic.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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} урона, оно введёт вас в критическое состояние, если его использовать!
13 changes: 2 additions & 11 deletions Resources/Prototypes/ADT/Datasets/tags.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -122,4 +113,4 @@
- Trash
- Wirecutter
- Wrench
- ADTTeaPack
- ADTTeaPack
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,4 @@
Piercing: 15
Structural: 100
soundHit:
path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg
path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg
Loading

0 comments on commit 5d89838

Please sign in to comment.