Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
70ef806
FUCK
LuciferEOS Apr 28, 2026
3187363
fixes & white
LuciferEOS May 1, 2026
1faad7a
shit2
LuciferEOS May 2, 2026
bb2feae
white wolf mark stuff (help)
LuciferEOS May 3, 2026
0b370ea
im going to fucking kill myself
LuciferEOS May 6, 2026
e412412
Merge branch 'Inky-Station:master' into werewolf
LuciferEOS May 14, 2026
ca15338
eh
LuciferEOS May 17, 2026
22dd2f6
Merge branch 'master' into werewolf
LuciferEOS May 31, 2026
b1d9589
fuck
LuciferEOS May 31, 2026
bf4cbab
black
LuciferEOS Jun 5, 2026
eb19867
beckon & alpha order
LuciferEOS Jun 9, 2026
4ef752a
Merge branch 'master' into werewolf
LuciferEOS Jun 9, 2026
d3a4344
Merge remote-tracking branch 'origin/master' into werewolf
LuciferEOS Jun 11, 2026
ae3397c
e
LuciferEOS Jun 12, 2026
d84c567
todo
LuciferEOS Jun 13, 2026
ee3a4c6
e (#5)
LuciferEOS Jun 15, 2026
c29b172
I LUV P1 ISSUES THAT REQUIRE ME TO ABANDON MY WORK
LuciferEOS Jun 16, 2026
4a98903
ee
LuciferEOS Jun 19, 2026
ba381eb
son
LuciferEOS Jul 2, 2026
4025f3a
Merge branch 'master' into werewolf
LuciferEOS Jul 25, 2026
114ef59
upstream
LuciferEOS Jul 25, 2026
522c0cd
merge fuckup
LuciferEOS Jul 25, 2026
605a181
e
LuciferEOS Jul 25, 2026
0e0e213
fuck
LuciferEOS Jul 25, 2026
32786bb
sec
LuciferEOS Jul 25, 2026
ce09eec
sec3
LuciferEOS Jul 25, 2026
16c6f4f
the call and locale
LuciferEOS Jul 26, 2026
9aecc72
what
LuciferEOS Jul 26, 2026
0c444dd
locale2
LuciferEOS Jul 26, 2026
ea94b56
stuff
LuciferEOS Jul 26, 2026
b6e79b2
final?
LuciferEOS Jul 26, 2026
89e9121
Merge branch 'Inky-Station:master' into werewolf
LuciferEOS Jul 26, 2026
adce804
?
LuciferEOS Aug 1, 2026
f533027
fix ambush
LuciferEOS Aug 1, 2026
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
3 changes: 3 additions & 0 deletions Content.Inky.Common/Events/Werewolf/WerewolfEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Content.Inky.Common.Events.Werewolf;

public readonly record struct SelectFirstMartialArtEvent(EntityUid Entity);
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Content.Goobstation.Common.Blob;
using Content.Goobstation.Server.Changeling.GameTicking.Rules;
using Content.Inky.Shared.Werewolf.Components;
using Content.Server.Administration.Managers;
using Content.Server.Administration.Systems;
using Content.Server.Antag;
using Content.Shared.Database;
using Content.Shared.Mind.Components;
using Content.Shared.Verbs;
using Content.Trauma.Common.Silicon;
using Robust.Shared.Player;
using Robust.Shared.Utility;

namespace Content.Inky.Server.Administration.Systems;

public sealed partial class InkyAdminVerbSystem
{
[Dependency] private AntagSelectionSystem _antag = default!;

private void OnGetAntagVerbs(ref GetAntagVerbsEvent args)
{
if (!HasComp<MindContainerComponent>(args.Target) || !TryComp<ActorComponent>(args.Target, out var targetActor))
return;

var targetPlayer = targetActor.PlayerSession;

args.Verbs.Verbs.Add(new()
{
Text = Loc.GetString("admin-verb-text-make-werewolf"),
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/_Inky/Actions/Werewolf/werewolf.rsi"), "howl"),
Act = () =>
{
_antag.ForceMakeAntag<WerewolfRuleComponent>(targetPlayer, "Werewolf");
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-werewolf"),
});
}
}
13 changes: 13 additions & 0 deletions Content.Inky.Server/Administration/Systems/InkyAdminVerbSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Content.Server.Administration.Systems;

namespace Content.Inky.Server.Administration.Systems;

public sealed partial class InkyAdminVerbSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<GetAntagVerbsEvent>(OnGetAntagVerbs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Linq;
using Content.Inky.Shared.Werewolf;
using Content.Inky.Shared.Werewolf.Components;
using Content.Shared.Chat;
using Content.Shared.Mind;
using Content.Shared.Mobs.Components;
using Robust.Shared.Utility;

namespace Content.Inky.Server.Werewolf.Systems;

public sealed partial class WerewolfAbilitiesSystem
{
/// <inheritdoc/>
public void InitializeBlack()
{
SubscribeLocalEvent<WerewolfAbilitiesComponent, WerewolfBeckonEvent>(OnBeckon);
SubscribeLocalEvent<WerewolfAbilitiesComponent, WerewolfBlackCallEvent>(OnCall);
}

private void OnBeckon(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfBeckonEvent args)
{
var locationName = FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString(uid));

var message = Loc.GetString("werewolf-beckon-message",
("name", MetaData(uid).EntityName),
("location", locationName));

_chat.TrySendInGameICMessage(uid, $"+l {message}", InGameICChatType.CollectiveMind, ChatTransmitRange.Normal); // holy goida IF ANYONE CHANGES LUNARMIND KEY LETTER CHANGE IT HERE TOO
args.Handled = true;
}

private void OnCall(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfBlackCallEvent args)
{
if (!_mind.TryGetMind(uid, out var leaderMind, out _)
|| !TryComp<WerewolfMindComponent>(leaderMind, out var leaderMindTakeTwo))
return;

var alphas = new List<(EntityUid Mind, EntityUid Body)> { (leaderMind, uid) };
var alphasMind = new HashSet<EntityUid> { leaderMind }; // has to be hashset bcuz bullshit

foreach (var alphaMind in leaderMindTakeTwo.PackMembers)
{
if (!alphasMind.Add(alphaMind)
|| !TryComp<MindComponent>(alphaMind, out var alphaMindIdk)
|| alphaMindIdk.OwnedEntity is not { } alphaBody
|| !HasComp<WerewolfAbilitiesComponent>(alphaBody))
continue;

alphas.Add((alphaMind, alphaBody));
}

// The original alpha needs to have 4 more alphas that hit the gym EVERY DAY to be on that grindset to do the call
if (alphas.Count < 5)
{
_popup.PopupClient(Loc.GetString("werewolf-black-call-fail-amount"), uid);
return;
}

foreach (var (wolfMindId, wolfBody) in alphas)
{
if (TryComp<WerewolfAbilitiesComponent>(wolfBody, out var wolfAbilities)
&& !wolfAbilities.Transfurmed)
{
RaiseLocalEvent(wolfBody, new TransfurmEvent(true));
}

if (!TryComp<WerewolfMindComponent>(wolfMindId, out var wolfMind)
|| !TryComp<MindComponent>(wolfMindId, out var mind)
|| mind.OwnedEntity is not { } transformedBody)
continue;

wolfMind.BlockTransfurm = true;

if (!TryComp<MobStateComponent>(transformedBody, out _)
|| !TryComp<MobThresholdsComponent>(transformedBody, out var thresholds))
continue;

foreach (var (health, state) in thresholds.Thresholds.ToArray())
{
_mobThresholds.SetMobStateThreshold(transformedBody, health * 2, state, thresholds);
}
}

if (_station.GetOwningStation(uid) is { } station)
_stationAlerts.SetLevel(station, "violet", true, true, true); // on a side note why the fuck is this shit not capitalised

var message = Loc.GetString("werewolf-black-call-success");
_chat.TrySendInGameICMessage(uid, $"+l {message}", InGameICChatType.CollectiveMind, ChatTransmitRange.Normal);
args.Handled = true;
RaiseLocalEvent(uid, new WerewolfActionRemoveEvent(args.Action)); // kill yourself
}
}
209 changes: 209 additions & 0 deletions Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Side.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
using System.Linq;
using Content.Goobstation.Shared.Changeling.Components;
using Content.Inky.Shared.Werewolf;
using Content.Inky.Shared.Werewolf.Components;
using Content.Medical.Shared.Wounds;
using Content.Shared.Body;
using Content.Shared.Body.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.DoAfter;
using Content.Shared.Popups;
using Robust.Shared.Prototypes;

namespace Content.Inky.Server.Werewolf.Systems;

/// <summary>
/// Handles side abilities and helpers for the werewolf
/// </summary>
public sealed partial class WerewolfAbilitiesSystem
{
public void InitializeWerewolfSide()
{
SubscribeLocalEvent<WerewolfAbilitiesComponent, EventWerewolfDevour>(TryDevour);
SubscribeLocalEvent<WerewolfAbilitiesComponent, WerewolfDevourDoAfterEvent>(DoDevour);
SubscribeLocalEvent<WerewolfAbilitiesComponent, EventWerewolfGut>(TryGut);
SubscribeLocalEvent<WerewolfAbilitiesComponent, WerewolfGutDoAfterEvent>(DoGut);
}
# region devour
private void TryDevour(EntityUid uid, WerewolfAbilitiesComponent component, EventWerewolfDevour args)
{
var target = args.Target;

if (HasComp<WerewolfBitComponent>(target))
{
_popup.PopupPredictedCursor(Loc.GetString("werewolf-devour-fail-devoured"), uid);
return;
}
if (!HasComp<AbsorbableComponent>(target)) // i mean... it works? also less wizden files changes
{
_popup.PopupPredicted(Loc.GetString("changeling-absorb-fail-unabsorbable"), uid, uid);
return;
}

if (HasComp<WerewolfAbilitiesComponent>(target))
{
_popup.PopupPredicted(Loc.GetString("werewolf-devour-fail-werewolf"), uid, uid); // no to eating each other
return;
}

var popupOthers = Loc.GetString("werewolf-devour-start", ("user", uid), ("target", target));
_popup.PopupPredicted(popupOthers, uid, uid, PopupType.LargeCaution);

var dargs = new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(4), new WerewolfDevourDoAfterEvent(), uid, target) // todo werewolf unhardcode duration
{
DistanceThreshold = 1.5f,
BreakOnDamage = true,
BreakOnHandChange = false,
BreakOnMove = true,
BreakOnWeightlessMove = true,
AttemptFrequency = AttemptFrequency.StartAndEnd,
MultiplyDelay = false,
};
_doAfter.TryStartDoAfter(dargs);
}

public ProtoId<DamageGroupPrototype> DevourDamage = "Brute"; // bro
private void DoDevour(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfDevourDoAfterEvent args)
{
if (args.Args.Target == null)
return;

var target = args.Args.Target.Value;

if (args.Cancelled
|| HasComp<WerewolfBitComponent>(target)
|| !TryComp<BodyComponent>(target, out var body))
return;

var dmg = new DamageSpecifier(_proto.Index(DevourDamage), 35); // todo werewolf unhardcode this
_damage.TryChangeDamage(target, dmg, true, true);
RipLimb(target, body);

var targetComp = EnsureComp<WerewolfBitComponent>(target);

if (!_mind.TryGetMind(uid, out var mindId, out _)
|| !TryComp<WerewolfMindComponent>(mindId, out var mindComp))
return;

mindComp.Currency += comp.AmountDevour;
mindComp.BittenPeople.Add(args.Args.Target.Value);
targetComp.BittenBy = mindComp;

_hunger.ModifyHunger(uid, +80); // todo werewolf maybe put as a var inside a comp or sdome shit
_audio.PlayPvs(comp.RipSound, uid);
}

private void TryGut(EntityUid uid, WerewolfAbilitiesComponent comp, EventWerewolfGut args)
{
var target = args.Target;

if (!HasComp<AbsorbableComponent>(target))
{
_popup.PopupEntity(Loc.GetString("changeling-absorb-fail-unabsorbable"), uid, uid);
return;
}

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

if (mind == null)
{
_popup.PopupEntity(Loc.GetString("werewolf-gut-fail-mind"), uid, uid);
return;
}

var popupOthers = Loc.GetString("werewolf-gut-start", ("user", uid), ("target", target)); // todo locale
_popup.PopupPredicted(popupOthers, uid, uid, PopupType.LargeCaution);

var dargs = new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(4), new WerewolfGutDoAfterEvent(), uid, target)// todo werewolf unhardcode duration
{
DistanceThreshold = 1.5f,
BreakOnDamage = true,
BreakOnHandChange = false,
BreakOnMove = true,
BreakOnWeightlessMove = true,
AttemptFrequency = AttemptFrequency.StartAndEnd,
MultiplyDelay = false,
};
_doAfter.TryStartDoAfter(dargs);
}

#endregion
#region helpers
private void DoGut(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfGutDoAfterEvent args)
{
if (args.Args.Target == null)
return;

var target = args.Args.Target.Value;

if (args.Cancelled
|| !TryComp<BodyComponent>(target, out var body))
return;

if (!TryRemoveOrgan(uid, target, out var removedOrgan))
return;

_blood.SpillAllSolutions(target);
if (_mind.TryGetMind(uid, out var mindId, out _) && TryComp<WerewolfMindComponent>(mindId, out var mindComp))
mindComp.Currency += comp.AmountGut;

_hunger.ModifyHunger(uid, +20); // todo werewolf maybe put this inside comp
_audio.PlayPvs(comp.RipSound, uid);
}

private bool TryRemoveOrgan(EntityUid user, EntityUid target, out EntityUid? removedOrgan) // shit was originally taken from devil shitcode but upstream broke a shitton of stuff
{
removedOrgan = null;

if (!TryComp<BodyComponent>(target, out var body))
return false;

var organs = _body.GetInternalOrgans((target, body))
.Where(organ => !HasComp<BrainComponent>(organ.Owner))
.ToList();

if (organs.Count < 1)
{
_popup.PopupEntity(Loc.GetString("werewolf-gut-no-organs-left"), user, user);
return false;
}

var nextOrgan = _gambling.Next(organs.Count); // idk
var picked = organs[nextOrgan];
removedOrgan = picked.Owner;

if (TryComp<OrganComponent>(picked.Owner, out var organComp))
_body.RemoveOrgan((target, body), new Entity<OrganComponent?>(picked.Owner, organComp)); // this is horrible
QueueDel(picked.Owner);

_popup.PopupEntity(Loc.GetString("werewolf-gut-success", ("user", user), ("target", target)), user, user);

return true;
}

private void RipLimb(EntityUid target, BodyComponent body)
{
var allOrgans = _body.GetOrgans((target, body));
var limbs = allOrgans // limbs are considered organs for some reason
.Where(organ =>
{
var category = _body.GetCategory(new Entity<OrganComponent?>(organ.Owner, organ.Comp));
return category == "ArmLeft" || category == "ArmRight"; // TODO WEREWOLF: DESHITCODE
})// i have PTSD from shitmed and inkymed looking at this shit above
.ToList();

if (limbs.Count <= 0)
return;

var nextOrgan = _gambling.Next(limbs.Count); // boo copypaste from TryRemoveOrgan
var picked = limbs[nextOrgan];

if (!TryComp<WoundableComponent>(picked.Owner, out var woundable)
|| !woundable.ParentWoundable.HasValue)
return;

_wound.AmputateWoundableSafely(woundable.ParentWoundable.Value, picked.Owner, woundable);
}
# endregion
}
Loading
Loading