Skip to content

Commit

Permalink
Merge pull request #278 from FoxxoTrystan/merge-fixes
Browse files Browse the repository at this point in the history
Cherry-Pick a bunch of fixes.
  • Loading branch information
FoxxoTrystan authored Oct 22, 2024
2 parents 6d10b37 + 549a020 commit 656c2fa
Show file tree
Hide file tree
Showing 22 changed files with 359 additions and 66 deletions.
5 changes: 5 additions & 0 deletions Content.Client/Chapel/SacrificialAltarSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Content.Shared.Chapel;

namespace Content.Client.Chapel;

public sealed class SacrificialAltarSystem : SharedSacrificialAltarSystem;
13 changes: 4 additions & 9 deletions Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@ public void SetProfile(HumanoidCharacterProfile? profile, int? slot)
UpdateSkinColor();
UpdateSpawnPriorityControls();
UpdateFlavorTextEdit();
UpdateCustomSpecieNameEdit();
UpdateAgeEdit();
UpdateEyePickers();
UpdateSaveButton();
Expand Down Expand Up @@ -1206,15 +1207,9 @@ private void UpdateNameEdit()

private void UpdateCustomSpecieNameEdit()
{
if (Profile == null)
return;

_customspecienameEdit.Text = Profile.Customspeciename ?? "";

if (!_prototypeManager.TryIndex<SpeciesPrototype>(Profile.Species, out var speciesProto))
return;

_ccustomspecienamecontainerEdit.Visible = speciesProto.CustomName;
var species = _species.Find(x => x.ID == Profile?.Species) ?? _species.First();
_customspecienameEdit.Text = string.IsNullOrEmpty(Profile?.Customspeciename) ? Loc.GetString(species.Name) : Profile.Customspeciename;
_ccustomspecienamecontainerEdit.Visible = species.CustomName;
}

private void UpdateFlavorTextEdit()
Expand Down
129 changes: 129 additions & 0 deletions Content.Server/Chapel/SacrificialAltarSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using Content.Server.Bible.Components;
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.Chapel;
using Content.Shared.DoAfter;
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.Chapel;

public sealed class SacrificialAltarSystem : SharedSacrificialAltarSystem
{
[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;

if (args.Cancelled || args.Handled || args.Args.Target is not { } target
|| !TryComp<PsionicComponent>(target, out var psionic)
|| !_mind.TryGetMind(target, out var _, out var _))
return;

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

// lower glimmer by a random amount
_glimmer.Glimmer -= (int) (ent.Comp.GlimmerReduction * psionic.CurrentAmplification);

if (ent.Comp.RewardPool is not null && _random.Prob(ent.Comp.BaseItemChance * psionic.CurrentDampening))
{
var proto = _proto.Index(_random.Pick(ent.Comp.RewardPool));
Spawn(proto.ToString(), Transform(ent).Coordinates);
}
// 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: false, 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))
{
_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,
BreakOnUserMove = true,
BreakOnTargetMove = true,
BreakOnWeightlessMove = true,
NeedHand = true
};
DoAfter.TryStartDoAfter(args, out ent.Comp.DoAfter);
}
}
6 changes: 2 additions & 4 deletions Content.Shared/Chapel/SacrificeDoAfterEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
using Content.Shared.DoAfter;

namespace Content.Shared.Chapel;
[Serializable, NetSerializable]
public sealed partial class SacrificeDoAfterEvent : SimpleDoAfterEvent
{

}
[Serializable, NetSerializable]
public sealed partial class SacrificeDoAfterEvent : SimpleDoAfterEvent { }
48 changes: 48 additions & 0 deletions Content.Shared/Chapel/SacrificialAltarComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Content.Shared.Random;
using Content.Shared.DoAfter;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.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/Psionics/heartbeat_fast.ogg");

[DataField]
public EntityUid? SacrificeStream;

/// <summary>
/// Base amount to reduce glimmer by, multiplied by the victim's Amplification stat.
/// </summary>
[DataField]
public float GlimmerReduction = 25;

[DataField]
public List<ProtoId<WeightedRandomEntityPrototype>>? RewardPool;

/// <summary>
/// The base chance to generate an item of power, multiplied by the victim's Dampening stat.
/// </summary>
[DataField]
public float BaseItemChance = 0.1f;
}
61 changes: 61 additions & 0 deletions Content.Shared/Chapel/SharedSacrificialAltarSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Linq;
using Content.Shared.Buckle.Components;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Verbs;

namespace Content.Shared.Chapel;

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

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

SubscribeLocalEvent<SacrificialAltarComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<SacrificialAltarComponent, BuckleChangeEvent>(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 BuckleChangeEvent args)
{
if (ent.Comp.DoAfter is not { } id)
return;

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
|| !TryComp<StrapComponent>(ent, out var strap)
|| 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)
{
if (strap.BuckledEntities.Count <= 0)
return null;

return strap.BuckledEntities.First();
}

protected virtual void AttemptSacrifice(Entity<SacrificialAltarComponent> ent, EntityUid user, EntityUid target) { }
}
Binary file modified Resources/Audio/Floof/Voice/Vulpkanin/gekker.ogg
Binary file not shown.
40 changes: 40 additions & 0 deletions Resources/Changelog/Changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7492,3 +7492,43 @@ Entries:
id: 6477
time: '2024-10-20T19:28:35.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1113
- author: VMSolidus
changes:
- type: Add
message: >-
Sacrificing Psions has been added. Psions can be sacrificed by
Epistemics upon an altar in order to dramatically reduce glimmer. The
more powerful the Psion to be sacrificed, the more glimmer is reduced.
id: 6478
time: '2024-10-20T21:34:05.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1110
- author: Aidenkrz
changes:
- type: Fix
message: Flavor text can be updated again.
id: 6479
time: '2024-10-21T00:24:18.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1119
- author: VMSolidus
changes:
- type: Add
message: Glacier is now on a planetary surface
id: 6480
time: '2024-10-21T00:41:02.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1115
- author: VMSolidus
changes:
- type: Fix
message: >-
Asterisk Station no longer spawns entombed in snow, and is now on top of
an ice sheet.
id: 6481
time: '2024-10-21T00:41:18.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1118
- author: Aidenkrz
changes:
- type: Fix
message: Custom specie name doesn't disappear in the editor anymore.
id: 6482
time: '2024-10-22T01:42:11.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1120
11 changes: 11 additions & 0 deletions Resources/Locale/en-US/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}!
27 changes: 26 additions & 1 deletion Resources/Maps/asterisk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,39 @@ entities:
- type: Broadphase
- type: OccluderTree
- type: Parallax
parallax: AngleStation
parallax: Snow
- type: MapAtmosphere
space: False
mixture:
volume: 2500
immutable: True
temperature: 213.15
moles:
- 21.824879
- 82.10312
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- 0
- type: Gravity
enabled: true
inherent: true
- type: LoadedMap
- uid: 2
components:
- type: MetaData
- type: Transform
pos: -0.5104167,-0.4739367
parent: 1
- type: PassiveDampening
linearDampening: 1
angularDampening: 1
- type: MapGrid
chunks:
0,0:
Expand Down
Loading

0 comments on commit 656c2fa

Please sign in to comment.