Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
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
}
}
211 changes: 211 additions & 0 deletions Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Side.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
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.Chemistry.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
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