Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port psionic sacraficing #1883

Merged
merged 10 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Content.Client/DeltaV/Chapel/SacrificialAltarSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Content.Shared.DeltaV.Chapel;

namespace Content.Client.DeltaV.Chapel;

public sealed class SacrificialAltarSystem : SharedSacrificialAltarSystem;
139 changes: 139 additions & 0 deletions Content.Server/DeltaV/Chapel/SacrificialAltarSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using Content.Server.Bible.Components;
using Content.Server.Nyanotrasen.Cloning;
using Content.Shared.Abilities.Psionics;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components;
using Content.Shared.Body.Systems;
using Content.Shared.Database;
using Content.Shared.DeltaV.Chapel;
using Content.Shared.DoAfter;
using Content.Shared.EntityTable;
using Content.Shared.Humanoid;
using Content.Shared.Mind;
using Content.Shared.Popups;
using Content.Shared.Psionics.Glimmer;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;

namespace Content.Server.DeltaV.Chapel;

public sealed class SacrificialAltarSystem : SharedSacrificialAltarSystem
{
[Dependency] private readonly EntityTableSystem _entityTable = default!;
[Dependency] private readonly GlimmerSystem _glimmer = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedBodySystem _body = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<SacrificialAltarComponent, SacrificeDoAfterEvent>(OnDoAfter);
}

private void OnDoAfter(Entity<SacrificialAltarComponent> ent, ref SacrificeDoAfterEvent args)
{
ent.Comp.SacrificeStream = _audio.Stop(ent.Comp.SacrificeStream);
ent.Comp.DoAfter = null;

var user = args.Args.User;

if (args.Cancelled || args.Handled || args.Args.Target is not {} target)
return;

if (!_mind.TryGetMind(target, out var mindId, out var mind))
return;

// prevent starting the doafter then mindbreaking to double dip
if (!HasComp<PsionicComponent>(target))
return;

_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(user):player} sacrificed {ToPrettyString(target):target} on {ToPrettyString(ent):altar}");

// lower glimmer by a random amount
_glimmer.Glimmer -= ent.Comp.GlimmerReduction.Next(_random);

// spawn all the loot
var proto = _proto.Index(ent.Comp.RewardPool);
var coords = Transform(ent).Coordinates;
foreach (var id in _entityTable.GetSpawns(proto.Table))
{
Spawn(id, coords);
}

// TODO GOLEMS: create a soul crystal and transfer mind into it

// finally gib the targets old body
if (TryComp<BodyComponent>(target, out var body))
_body.GibBody(target, gibOrgans: true, body, launchGibs: true);
else
QueueDel(target);
}

protected override void AttemptSacrifice(Entity<SacrificialAltarComponent> ent, EntityUid user, EntityUid target)
{
if (ent.Comp.DoAfter != null)
return;

// can't sacrifice yourself
if (user == target)
{
_popup.PopupEntity(Loc.GetString("altar-failure-reason-self"), ent, user, PopupType.SmallCaution);
return;
}

// you need to be psionic OR bible user
if (!HasComp<PsionicComponent>(user) && !HasComp<BibleUserComponent>(user))
{
_popup.PopupEntity(Loc.GetString("altar-failure-reason-user"), ent, user, PopupType.SmallCaution);
return;
}

// and no golems or familiars or whatever should be sacrificing
if (!HasComp<HumanoidAppearanceComponent>(user))
{
_popup.PopupEntity(Loc.GetString("altar-failure-reason-user-humanoid"), ent, user, PopupType.SmallCaution);
return;
}

// prevent psichecking SSD people...
// notably there is no check in OnDoAfter so you can't alt f4 to survive being sacrificed
if (!HasComp<ActorComponent>(target) || _mind.GetMind(target) == null)
{
_popup.PopupEntity(Loc.GetString("altar-failure-reason-target-catatonic", ("target", target)), ent, user, PopupType.SmallCaution);
return;
}

// TODO: there should be a penalty to the user for psichecking like this
if (!HasComp<PsionicComponent>(target))
{
_popup.PopupEntity(Loc.GetString("altar-failure-reason-target", ("target", target)), ent, user, PopupType.SmallCaution);
return;
}

if (!HasComp<HumanoidAppearanceComponent>(target) && !HasComp<MetempsychosisKarmaComponent>(target))
{
_popup.PopupEntity(Loc.GetString("altar-failure-reason-target-humanoid", ("target", target)), ent, user, PopupType.SmallCaution);
return;
}

_popup.PopupEntity(Loc.GetString("altar-sacrifice-popup", ("user", user), ("target", target)), ent, PopupType.LargeCaution);

ent.Comp.SacrificeStream = _audio.PlayPvs(ent.Comp.SacrificeSound, ent)?.Entity;

var ev = new SacrificeDoAfterEvent();
var args = new DoAfterArgs(EntityManager, user, ent.Comp.SacrificeTime, ev, target: target, eventTarget: ent)
{
BreakOnDamage = true,
NeedHand = true
};
DoAfter.TryStartDoAfter(args, out ent.Comp.DoAfter);
}
}
47 changes: 47 additions & 0 deletions Content.Shared/DeltaV/Chapel/SacrificialAltarComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Content.Shared.Destructible.Thresholds;
using Content.Shared.DoAfter;
using Content.Shared.EntityTable;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;

namespace Content.Shared.DeltaV.Chapel;

/// <summary>
/// Altar that lets you sacrifice psionics to lower glimmer by a large amount.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSacrificialAltarSystem))]
public sealed partial class SacrificialAltarComponent : Component
{
/// <summary>
/// DoAfter for an active sacrifice.
/// </summary>
[DataField]
public DoAfterId? DoAfter;

/// <summary>
/// How long it takes to sacrifice someone once they die.
/// This is the window to interrupt a sacrifice if you want glimmer to stay high, or need the psionic to be revived.
/// </summary>
[DataField]
public TimeSpan SacrificeTime = TimeSpan.FromSeconds(8.35);

[DataField]
public SoundSpecifier SacrificeSound = new SoundPathSpecifier("/Audio/DeltaV/Effects/clang2.ogg");

[DataField]
public EntityUid? SacrificeStream;

/// <summary>
/// Random amount to reduce glimmer by.
/// </summary>
[DataField]
public MinMax GlimmerReduction = new(30, 60);

[DataField]
public ProtoId<EntityTablePrototype> RewardPool = "PsionicSacrificeRewards";
}

[Serializable, NetSerializable]
public sealed partial class SacrificeDoAfterEvent : SimpleDoAfterEvent;
70 changes: 70 additions & 0 deletions Content.Shared/DeltaV/Chapel/SharedSacrificialAltarSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Content.Shared.Buckle;
using Content.Shared.Buckle.Components;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Verbs;

namespace Content.Shared.DeltaV.Chapel;

public abstract class SharedSacrificialAltarSystem : EntitySystem
{
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
[Dependency] protected readonly SharedDoAfterSystem DoAfter = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<SacrificialAltarComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<SacrificialAltarComponent, UnstrappedEvent>(OnUnstrapped);
SubscribeLocalEvent<SacrificialAltarComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
}

private void OnExamined(Entity<SacrificialAltarComponent> ent, ref ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("altar-examine"));
}

private void OnUnstrapped(Entity<SacrificialAltarComponent> ent, ref UnstrappedEvent args)
{
if (ent.Comp.DoAfter is {} id)
{
DoAfter.Cancel(id);
ent.Comp.DoAfter = null;
}
}

private void OnGetVerbs(Entity<SacrificialAltarComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || ent.Comp.DoAfter != null)
return;

if (!TryComp<StrapComponent>(ent, out var strap))
return;

if (GetFirstBuckled(strap) is not {} target)
return;

var user = args.User;
args.Verbs.Add(new AlternativeVerb()
{
Act = () => AttemptSacrifice(ent, user, target),
Text = Loc.GetString("altar-sacrifice-verb"),
Priority = 2
});
}

private EntityUid? GetFirstBuckled(StrapComponent strap)
{
foreach (var entity in strap.BuckledEntities)
{
return entity;
}

return null;
}

protected virtual void AttemptSacrifice(Entity<SacrificialAltarComponent> ent, EntityUid user, EntityUid target)
{
}
}
11 changes: 11 additions & 0 deletions Resources/Locale/en-US/deltav/chapel/altar.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
altar-examine = [color=purple]This altar can be used to sacrifice Psionics.[/color]
altar-sacrifice-verb = Sacrifice

altar-failure-reason-self = You can't sacrifice yourself!
altar-failure-reason-user = You are not psionic or clerically trained!
altar-failure-reason-user-humanoid = You are not a humanoid!
altar-failure-reason-target = {CAPITALIZE(THE($target))} {CONJUGATE-BE($target)} not psionic!
altar-failure-reason-target-humanoid = {CAPITALIZE(THE($target))} {CONJUGATE-BE($target)} not a humanoid!
altar-failure-reason-target-catatonic = {CAPITALIZE(THE($target))} {CONJUGATE-BE($target)} braindead!

altar-sacrifice-popup = {$user} starts to sacrifice {$target}!
11 changes: 10 additions & 1 deletion Resources/Prototypes/Entities/Structures/Furniture/altar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,17 @@
- TableLayer
- type: Sprite
snapCardinals: true
- type: Climbable
#- type: Climbable # DeltaV: remove climbable since it conflicts with strap
- type: Clickable
# Begin DeltaV additions: Sacrificing psionics
- type: SacrificialAltar
- type: Strap
position: Down
rotation: -90
- type: GuideHelp
guides:
- AltarsGolemancy
# End DeltaV additions

- type: entity
id: AltarNanotrasen
Expand Down
2 changes: 1 addition & 1 deletion Resources/Prototypes/Guidebook/science.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- Xenoarchaeology
- Robotics
- Psionics # Nyanotrasen - Psionics guidebook
# - AltarsGolemancy # When it's added # Nyanotrasen - Golemancy guidebook
- AltarsGolemancy # Nyanotrasen - Golemancy guidebook
- ReverseEngineering # Nyanotrasen - Reverse Engineering guidebook
- GlimmerCreatures # DeltaV

Expand Down
8 changes: 4 additions & 4 deletions Resources/Prototypes/Nyanotrasen/Guidebook/epistemics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
name: guide-entry-psionics
text: "/ServerInfo/Nyanotrasen/Guidebook/Epistemics/Psionics.xml"

# - type: guideEntry # When it's added
# id: AltarsGolemancy
# name: guide-entry-altars-golemancy
# text: "/ServerInfo/Nyanotrasen/Guidebook/Epistemics/Altar.xml"
- type: guideEntry
id: AltarsGolemancy
name: guide-entry-altars-golemancy
text: "/ServerInfo/Nyanotrasen/Guidebook/Epistemics/Altar.xml"

- type: guideEntry
id: ReverseEngineering
Expand Down
56 changes: 35 additions & 21 deletions Resources/Prototypes/Nyanotrasen/psionicArtifacts.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
- type: weightedRandom
id: PsionicArtifactPool
weights:
ClothingHandsDispelGloves: 1
ClothingEyesTelegnosisSpectacles: 1
ClothingHandsGlovesColorYellowUnsulated: 1
PonderingOrbTelepathic: 1
ClothingShoesBootsMagBlinding: 0.5
LidOSaws: 0.25
BedsheetInvisibilityCloak: 0.15
# WeaponWandPolymorphCarp: 0.05
# WeaponWandPolymorphMonkey: 0.05
# WeaponWandFireball: 0.025
# WeaponWandDeath: 0.001
# WeaponWandPolymorphDoor: 0.05
SpawnSpellbook: 0.025
ForceWallSpellbook: 0.025
BlinkBook: 0.025
SmiteBook: 0.00025
KnockSpellbook: 0.025
FireballSpellbook: 0.01
# Items to spawn when sacrificing a psionic
- type: entityTable
id: PsionicSacrificeRewards
table: !type:AllSelector
children:
- id: MaterialBluespace1
amount: !type:RangeNumberSelector
range: 1, 4
- !type:NestedSelector
tableId: PsionicArtifacts
prob: 0.5
deltanedas marked this conversation as resolved.
Show resolved Hide resolved

- type: entityTable
id: PsionicArtifacts
table: !type:GroupSelector
children:
- id: ClothingHandsDispelGloves
- id: ClothingEyesTelegnosisSpectacles
- id: ClothingHandsGlovesColorYellowUnsulated
- id: ClothingShoesBootsMagBlinding
weight: 0.5
- id: BedsheetInvisibilityCloak
weight: 0.15
- id: SpawnSpellbook
weight: 0.025
- id: ForceWallSpellbook
weight: 0.025
- id: BlinkBook
weight: 0.025
- id: SmiteBook
weight: 0.00025
- id: KnockSpellbook
weight: 0.025
- id: FireballSpellbook
weight: 0.01
Loading
Loading