diff --git a/Content.Inky.Common/Events/Werewolf/WerewolfEvents.cs b/Content.Inky.Common/Events/Werewolf/WerewolfEvents.cs new file mode 100644 index 00000000000..e728822372c --- /dev/null +++ b/Content.Inky.Common/Events/Werewolf/WerewolfEvents.cs @@ -0,0 +1,3 @@ +namespace Content.Inky.Common.Events.Werewolf; + +public readonly record struct SelectFirstMartialArtEvent(EntityUid Entity); diff --git a/Content.Inky.Server/Administration/Systems/InkyAdminVerbSystem.Antag.cs b/Content.Inky.Server/Administration/Systems/InkyAdminVerbSystem.Antag.cs new file mode 100644 index 00000000000..e0e0a7c9a7c --- /dev/null +++ b/Content.Inky.Server/Administration/Systems/InkyAdminVerbSystem.Antag.cs @@ -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(args.Target) || !TryComp(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(targetPlayer, "Werewolf"); + }, + Impact = LogImpact.High, + Message = Loc.GetString("admin-verb-make-werewolf"), + }); + } +} diff --git a/Content.Inky.Server/Administration/Systems/InkyAdminVerbSystem.cs b/Content.Inky.Server/Administration/Systems/InkyAdminVerbSystem.cs new file mode 100644 index 00000000000..6862932bca5 --- /dev/null +++ b/Content.Inky.Server/Administration/Systems/InkyAdminVerbSystem.cs @@ -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(OnGetAntagVerbs); + } +} diff --git a/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Black.cs b/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Black.cs new file mode 100644 index 00000000000..7286eaec1f4 --- /dev/null +++ b/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Black.cs @@ -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 +{ + /// + public void InitializeBlack() + { + SubscribeLocalEvent(OnBeckon); + SubscribeLocalEvent(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(leaderMind, out var leaderMindTakeTwo)) + return; + + var alphas = new List<(EntityUid Mind, EntityUid Body)> { (leaderMind, uid) }; + var alphasMind = new HashSet { leaderMind }; // has to be hashset bcuz bullshit + + foreach (var alphaMind in leaderMindTakeTwo.PackMembers) + { + if (!alphasMind.Add(alphaMind) + || !TryComp(alphaMind, out var alphaMindIdk) + || alphaMindIdk.OwnedEntity is not { } alphaBody + || !HasComp(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(wolfBody, out var wolfAbilities) + && !wolfAbilities.Transfurmed) + { + RaiseLocalEvent(wolfBody, new TransfurmEvent(true)); + } + + if (!TryComp(wolfMindId, out var wolfMind) + || !TryComp(wolfMindId, out var mind) + || mind.OwnedEntity is not { } transformedBody) + continue; + + wolfMind.BlockTransfurm = true; + + if (!TryComp(transformedBody, out _) + || !TryComp(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 + } +} diff --git a/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Side.cs b/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Side.cs new file mode 100644 index 00000000000..64b425bade8 --- /dev/null +++ b/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.Side.cs @@ -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; + +/// +/// Handles side abilities and helpers for the werewolf +/// +public sealed partial class WerewolfAbilitiesSystem +{ + public void InitializeWerewolfSide() + { + SubscribeLocalEvent(TryDevour); + SubscribeLocalEvent(DoDevour); + SubscribeLocalEvent(TryGut); + SubscribeLocalEvent(DoGut); + } + # region devour + private void TryDevour(EntityUid uid, WerewolfAbilitiesComponent component, EventWerewolfDevour args) + { + var target = args.Target; + + if (HasComp(target)) + { + _popup.PopupPredictedCursor(Loc.GetString("werewolf-devour-fail-devoured"), uid); + return; + } + if (!HasComp(target)) // i mean... it works? also less wizden files changes + { + _popup.PopupPredicted(Loc.GetString("changeling-absorb-fail-unabsorbable"), uid, uid); + return; + } + + if (HasComp(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 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(target) + || !TryComp(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(target); + + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(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(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(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(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(target, out var body)) + return false; + + var organs = _body.GetInternalOrgans((target, body)) + .Where(organ => !HasComp(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(picked.Owner, out var organComp)) + _body.RemoveOrgan((target, body), new Entity(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(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(picked.Owner, out var woundable) + || !woundable.ParentWoundable.HasValue) + return; + + _wound.AmputateWoundableSafely(woundable.ParentWoundable.Value, picked.Owner, woundable); + } + # endregion +} diff --git a/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.cs b/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.cs new file mode 100644 index 00000000000..af54fa6505b --- /dev/null +++ b/Content.Inky.Server/Werewolf/Systems/WerewolfAbilitiesSystem.cs @@ -0,0 +1,195 @@ +using Content.Inky.Common.Events.Werewolf; +using Content.Inky.Shared.Werewolf; +using Content.Inky.Shared.Werewolf.Components; +using Content.Inky.Shared.Werewolf.Systems; +using Content.Medical.Shared.Wounds; +using Content.Server.AlertLevel; +using Content.Server.Chat.Systems; +using Content.Server.Mind; +using Content.Server.Pinpointer; +using Content.Server.Polymorph.Systems; +using Content.Server.Popups; +using Content.Server.Station.Systems; +using Content.Server.Store.Systems; +using Content.Shared.Actions; +using Content.Shared.Body; +using Content.Shared.Body.Systems; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Damage.Systems; +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Content.Shared.Fluids; +using Content.Shared.Mobs.Systems; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Polymorph; +using Content.Shared.Store; +using Content.Shared.Store.Components; +using Robust.Server.GameObjects; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Inky.Server.Werewolf.Systems; + +public sealed partial class WerewolfAbilitiesSystem : EntitySystem +{ + [Dependency] private PolymorphSystem _polymorph = default!; + [Dependency] private StoreSystem _store = default!; + [Dependency] private PopupSystem _popup = default!; + [Dependency] private MindSystem _mind = default!; + [Dependency] private HungerSystem _hunger = default!; + + // holy fuck + [Dependency] private SharedDoAfterSystem _doAfter = default!; + [Dependency] private IPrototypeManager _proto = default!; + [Dependency] private DamageableSystem _damage = default!; + [Dependency] private SharedBloodstreamSystem _blood = default!; + [Dependency] private BodySystem _body = default!; + [Dependency] private IRobustRandom _gambling = default!; + [Dependency] private WoundSystem _wound = default!; + [Dependency] private SharedAudioSystem _audio = default!; + [Dependency] private NavMapSystem _navMap = default!; + [Dependency] private ChatSystem _chat = default!; + [Dependency] private AlertLevelSystem _stationAlerts = default!; + [Dependency] private StationSystem _station = default!; + [Dependency] private MobThresholdSystem _mobThresholds = default!; + [Dependency] private ActionContainerSystem _actionContainer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(TryTransfurm); + SubscribeLocalEvent(OnChangeType); + SubscribeLocalEvent(OnOpenStore); + SubscribeLocalEvent(OnPolymorphed); + SubscribeLocalEvent(OnActionRemove); + + InitializeWerewolfSide(); + InitializeBlack(); + } + + # region basic handlers + private void TryTransfurm(EntityUid uid, + WerewolfAbilitiesComponent component, + TransfurmEvent args) + { + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp)) + return; + + SyncMind(uid, component, mindComp); + + if (mindComp.BlockTransfurm) + { + _popup.PopupEntity(Loc.GetString("werewolf-transfurm-block"), uid, uid); + args.Handled = true; + return; + } + + if (!args.Forced && mindComp.Accumulator < mindComp.TransfurmOnCommandDelay) + { + _popup.PopupEntity(Loc.GetString("werewolf-transfurm-cooldown"), uid, uid); + args.Handled = true; + return; + } + + if (component.Transfurmed) + { + component.Transfurmed = false; + mindComp.TransfurmReady = false; + _polymorph.Revert(uid); + args.Handled = true; + mindComp.Accumulator = 0f; + return; + } + + component.Transfurmed = true; + mindComp.TransfurmReady = false; + _polymorph.PolymorphEntity(uid, component.CurrentMutation); + component.Transfurmed = false; // trust this is really important, the fucking polymorph is shit!!!! + mindComp.Accumulator = 0f; + args.Handled = true; + } + + private void OnPolymorphed(EntityUid uid, WerewolfAbilitiesComponent comp, PolymorphedEvent args) + { + if (!comp.Transfurmed) + { + _polymorph.CopyPolymorphComponent(uid, args.NewEntity); + + if (TryComp(uid, out var oldHunger)) // Transfer hunger value + _hunger.SetHunger(args.NewEntity, _hunger.GetHunger(oldHunger)); + return; + } + + if (TryComp(uid, out var oldHungerTakeTwo)) // Transfer hunger value + _hunger.SetHunger(args.NewEntity, _hunger.GetHunger(oldHungerTakeTwo)); + + var ev = new SelectFirstMartialArtEvent(args.NewEntity); // when you polymorph, it resets your current selected martial art + RaiseLocalEvent(ev); // this is a very lazy solution but hey it works + } + + private void OnOpenStore(Entity ent, ref EventWerewolfOpenStore args) + { + if (ent.Comp.Transfurmed) + return; + + WerewolfMindComponent? mindComp = null; + if (_mind.TryGetMind(ent, out var mindId, out _) && TryComp(mindId, out mindComp)) + SyncMind(ent, ent.Comp, mindComp); + + if (!TryComp(ent, out var store)) + return; + + // ok hear me out + // when you do shit in the WW form that gives you points, it saves in mind and then the next time you open store it adds up + // you HAVE to do ts because why? POLYMORPH IS FUCKING SHIT OF COURSE! ig you can store the old uid for store and shit but whatever + if (mindComp != null) + { + if (mindComp.Currency > 0) + { + _store.TryAddCurrency(new Dictionary {{ "Fury", mindComp.Currency }}, ent); + mindComp.Currency = 0; + } + } + + _store.ToggleUi(ent, ent, store); + ent.Comp.StoreOpened = true; + } + + private void OnChangeType(EntityUid uid, WerewolfAbilitiesComponent comp, EventWerewolfChangeType args) + { + comp.CurrentMutation = args.WerewolfType; + Dirty(uid, comp); + + if (_mind.TryGetMind(uid, out var mindId, out _) && TryComp(mindId, out var mindComp)) + mindComp.CurrentMutation = args.WerewolfType; + + _popup.PopupEntity(Loc.GetString("werewolf-mutation-changed"), uid, uid); + + args.Handled = true; + } + + private void OnActionRemove(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfActionRemoveEvent args) + { + _actionContainer.RemoveAction(args.ActionEnt); + } + + private void SyncMind(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfMindComponent mindComp) // oh my god brother todo werewolf rename to be better + { + if (mindComp.CurrentMutation is { } currentMutation + && comp.CurrentMutation != currentMutation) + { + comp.CurrentMutation = currentMutation; + Dirty(uid, comp); + } + + var store = EnsureComp(uid); + foreach (var category in mindComp.StoreCategories) + store.Categories.Add(category); + } + + #endregion +} diff --git a/Content.Inky.Server/Werewolf/Systems/WerewolfRuleSystem.cs b/Content.Inky.Server/Werewolf/Systems/WerewolfRuleSystem.cs new file mode 100644 index 00000000000..f6758647beb --- /dev/null +++ b/Content.Inky.Server/Werewolf/Systems/WerewolfRuleSystem.cs @@ -0,0 +1,158 @@ +using System.Text; +using Content.Inky.Shared.Werewolf; +using Content.Inky.Shared.Werewolf.Components; +using Content.Server.Antag; +using Content.Server.GameTicking.Rules; +using Content.Server.Mind; +using Content.Shared.Mind; +using Content.Server.Objectives; +using Content.Shared.Actions; +using Content.Shared.EntityEffects; +using Content.Shared.EntityEffects.Effects; +using Content.Shared.Roles; +using Content.Shared.Roles.Components; +using Content.Shared.Store; +using Content.Shared.Store.Components; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Inky.Server.Werewolf.Systems; + +public sealed partial class WerewolfRuleSystem : GameRuleSystem +{ + [Dependency] private MindSystem _mind = default!; + [Dependency] private AntagSelectionSystem _antag = default!; + [Dependency] private SharedRoleSystem _role = default!; + [Dependency] private ActionContainerSystem _actions = default!; + [Dependency] private SharedEntityEffectsSystem _effects = default!; + [Dependency] private ObjectivesSystem _objectives = default!; + + public readonly SoundSpecifier BriefingSound = new SoundPathSpecifier("/Audio/_Inky/Antag/Werewolf/werewolf_start.ogg"); + + public readonly ProtoId WerewolfPrototypeId = "Werewolf"; + + public readonly ProtoId Currency = "Fury"; + + public readonly int StartingCurrency = 2; // to buy either regen or ambush, choose your game + + [ValidatePrototypeId] EntProtoId mindRole = "MindRoleWerewolf"; + + public readonly ProtoId WerewolfSkills = "WerewolfSkills"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSelectAntag); + SubscribeLocalEvent(OnTextPrepend); + + SubscribeLocalEvent(OnInfectionFinished); // goida + } + + private void OnSelectAntag(EntityUid uid, WerewolfRuleComponent comp, ref AfterAntagEntitySelectedEvent args) + { + MakeWerewolf(args.EntityUid, comp); + } + + /// + /// Makes the entity into a werewolf. + /// + /// EntityUid of an entity that is going to become a werewolf + /// WerewolfRule + /// Can this werewolf evolve into other werewolf types? + /// Should this werewolf have access to the blackappentice category? // todo werewolf UNFUCK ME + /// + public bool MakeWerewolf(EntityUid target, + WerewolfRuleComponent rule, + bool evolution = true, // todo werewolf maybe rename? first thing that came into my mind + bool apprentice = false) + { + if (!_mind.TryGetMind(target, out var mindId, out var mind)) + return false; + + _role.MindAddRole(mindId, mindRole.Id, mind, true); + + var briefing = Loc.GetString("werewolf-role-greeting"); + var briefingShort = Loc.GetString("werewolf-role-greeting-short"); + + if (_role.MindHasRole(mindId, out var mr)) + AddComp(mr.Value, new RoleBriefingComponent { Briefing = briefingShort }, overwrite: true); + + EnsureComp(target, out var werewolfComp); + EnsureComp(mindId, out var werewolfMind); + + foreach (var action in werewolfComp.WerewolfActions) + { + if (!werewolfMind.UnlockedActions.Contains(action)) + werewolfMind.UnlockedActions.Add(action); + + _actions.AddAction(mindId, action); + } + + // add store + + var store = EnsureComp(target); + if (evolution) + { + foreach (var category in rule.StoreCategories) + store.Categories.Add(category); + } + + if (apprentice) + store.Categories.Add(rule.StoreApprentice); + + store.Categories.Add(rule.StoreSide); // maybe its better to make its own bool for it too? but if both evo & side is off, then its no point in adding a store at all + store.CurrencyWhitelist.Add(Currency); + store.Balance.Add(Currency, StartingCurrency); + + rule.WerewolfMinds.Add(mindId); + _antag.SendBriefing(target, briefing, Color.Brown, BriefingSound); + return true; + } + + private void OnInfectionFinished(ref WerewolfInfectionFinishedEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var rule, out _)) + { + RemComp(ev.Entity); + EnsureComp(ev.Entity); + MakeWerewolf(ev.Entity, rule, false, true); + + _effects.ApplyEffects(ev.Entity, [new NestedEffect { Proto = WerewolfSkills }], predicted: false); // :face_holding_back_tears: + + return; + } + } + + private void OnTextPrepend(Entity ent, ref ObjectivesTextPrependEvent args) + { + var sb = new StringBuilder(); + + foreach (var mindId in ent.Comp.WerewolfMinds) + { + if (!TryComp(mindId, out var werewolf) + || !TryComp(mindId, out var mind)) + continue; + + var name = _objectives.GetTitle((mindId, mind), Name(mind.OwnedEntity ?? mindId)); + sb.AppendLine($"{name} bit [color=red]{werewolf.BittenPeople.Count}[/color] people."); // idfc + + if (werewolf.PackMembers.Count == 0) + continue; + + var pack = new List(); + foreach (var packMind in werewolf.PackMembers) + { + if (!TryComp(packMind, out var packMind1)) + continue; + + pack.Add(_objectives.GetTitle((packMind, packMind1), Name(packMind1.OwnedEntity ?? packMind))); + } + + sb.AppendLine($"{name}'s pack: {string.Join(", ", pack)}."); + } + + args.Text = sb.ToString(); + } +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfAbilitiesComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfAbilitiesComponent.cs new file mode 100644 index 00000000000..59aaed88fd4 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfAbilitiesComponent.cs @@ -0,0 +1,46 @@ +using Content.Shared.Polymorph; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Inky.Shared.Werewolf.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class WerewolfAbilitiesComponent : Component +{ + [DataField] public SoundSpecifier ShriekSound = new SoundPathSpecifier("/Audio/_Inky/Antag/Werewolf/howl.ogg"); + [DataField] public SoundSpecifier DistantSound = new SoundPathSpecifier("/Audio/_Inky/Antag/Werewolf/howl.ogg"); // todo werewolf + [DataField] public SoundSpecifier RipSound = new SoundPathSpecifier("/Audio/Effects/gib1.ogg"); + + public readonly List WerewolfActions = new() + { + "ActionWerewolfTransfurm", + "ActionWerewolfOpenMutationStore", + "ActionWerewolfAbsorb", + "ActionWerewolfHowl" + }; + + [DataField, AutoNetworkedField] + public bool Transfurmed; + + [DataField] + public bool StoreOpened = true; // todo werewolf ungoida it, tie it to the mind and not the body you chud i fucking hate you future me raagh + // fuck you piece of shit previous me, why the fuck are half of the shit broken + // fuck you both why the fuck did the ww use changeling rule?? why did you let that pass you fucking chud previous me - dr. autism APR 28 2026 + + [DataField, AutoNetworkedField] + public ProtoId CurrentMutation = string.Empty; + + /// + /// Amount of points given per devour action performed of a person + /// + [DataField] + public int AmountDevour = 2; + + /// + /// Amount of points given per gut action performed + /// + [DataField] + public int AmountGut = 1; + +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfActionComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfActionComponent.cs new file mode 100644 index 00000000000..e3a09dab214 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfActionComponent.cs @@ -0,0 +1,20 @@ +using Robust.Shared.GameStates; + +namespace Content.Inky.Shared.Werewolf.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class WerewolfActionComponent : Component +{ + + [DataField] + public float HungerCost = 30f; + + [DataField] + public bool RequireTransfurmed = false; + + [DataField] + public LocId NotTransfurmedPopup = "werewolf-action-fail-transfurmed"; + + [DataField] + public LocId NoHungerPopup = "werewolf-action-fail-hunger"; +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfBequeathedComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfBequeathedComponent.cs new file mode 100644 index 00000000000..69881258fc9 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfBequeathedComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Store; +using Robust.Shared.Prototypes; + +namespace Content.Inky.Shared.Werewolf.Components; + +/// +/// On death, entity with this component will be force-mutated into a black wolf. +/// +[RegisterComponent] +public sealed partial class WerewolfBequeathedComponent : Component +{ + [DataField] public WerewolfMindComponent? OriginalLeader; + public readonly ProtoId Store = new("WerewolfBlack"); // goida +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfBitComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfBitComponent.cs new file mode 100644 index 00000000000..d08f7f34d6d --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfBitComponent.cs @@ -0,0 +1,27 @@ +namespace Content.Inky.Shared.Werewolf.Components; + +/// +/// Marks the person as bitten by a werewolf +/// this is given when an entity is a target for the werewolfdevour & other path specific bitings +/// +[RegisterComponent] +public sealed partial class WerewolfBitComponent : Component // todo loc strings for popups? +{ + [DataField] public WerewolfMindComponent? BittenBy; + + /// + /// If the entity is in the proccess of turning into a werewolf + /// + [DataField] + public bool Infected; + + [ViewVariables] + public float Accumulator = 0f; + + /// + /// After what time should the entity become a werewolf if bitten + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float LycTimer = 30f; // todo 600 +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfInfectionImmuneComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfInfectionImmuneComponent.cs new file mode 100644 index 00000000000..f73daa31b89 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfInfectionImmuneComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Inky.Shared.Werewolf.Components; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class WerewolfInfectionImmuneComponent : Component +{ + +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfMarkedComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfMarkedComponent.cs new file mode 100644 index 00000000000..8dac045a843 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfMarkedComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Inky.Shared.Werewolf.Components; + +/// +/// Marks a werewolf that it is being hunted by another +/// +[RegisterComponent] +public sealed partial class WerewolfMarkedComponent : Component +{ + [DataField] + public EntityUid MarkedBy; +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfMindComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfMindComponent.cs new file mode 100644 index 00000000000..1248024484d --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfMindComponent.cs @@ -0,0 +1,88 @@ +using Content.Shared.Polymorph; +using Content.Shared.Store; +using Robust.Shared.Prototypes; + +namespace Content.Inky.Shared.Werewolf.Components; + +// fucking KILL YOURSELF!!!! +[RegisterComponent] +public sealed partial class WerewolfMindComponent : Component // todo werewolf debloat? +{ + [DataField] + public List BittenPeople = new(); // would be used in the manifest TODO WEREWOLF + + /// + /// Used by the black wolf to show which entities were turned into werewolves by him. + /// Stores MIND ent uids, not body uids, bodies change on polymorph, minds dont. + /// + [DataField] + public List PackMembers = new(); + + /// + /// The ent currently being hunted by this werewolf + /// + [DataField] + public EntityUid? CurrentMarkedVictim; + + /// + /// If true, this werewolf wouldnt be counted for marking by other wolves + /// + [DataField] + public bool MarkImmune; // also holy shit this is starting to look like a bloated comp + + [DataField] + public List UnlockedActions = new(); + + [DataField] + public int Currency; // needed becasue polymorph & store shitcode + + [DataField] + public ProtoId? CurrentMutation; + + [DataField] + public HashSet> StoreCategories = new(); + #region transform + + /// + /// Transforms the werewolf automatically after the timer passes + /// + [DataField] + public float TransfurmCycle = 90; // todo werewolf 600 + + /// + /// After what time should the warning popup appear + /// + [DataField] + public float TransfurmWarnDelay = 60f; + + /// + /// After what amount of time can the entity transfurm on command again + /// + [DataField] + public float TransfurmOnCommandDelay = 30f; + + /// + /// Can you transfurm right now + /// + [DataField] + public bool TransfurmReady; + + [DataField] + public bool BlockTransfurm; + + [DataField] + public bool HasWarned; // to not spam shit + + [ViewVariables] + public LocId TransfurmPopup = "werewolf-transfurm-warn"; + + [ViewVariables] + public LocId TransfurmReadyPopup = "werewolf-transfurm-ready"; + + [ViewVariables] + public float Accumulator = 0f; + + [ViewVariables] // supriisngly used for marked guys + public float AccumulatorPopup = 0f; + #endregion +} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfRoleComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfRoleComponent.cs new file mode 100644 index 00000000000..64d428542b4 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfRoleComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Inky.Shared.Werewolf.Components; + +[RegisterComponent] +public sealed partial class WerewolfRoleComponent : Component {} diff --git a/Content.Inky.Shared/Werewolf/Components/WerewolfRuleComponent.cs b/Content.Inky.Shared/Werewolf/Components/WerewolfRuleComponent.cs new file mode 100644 index 00000000000..73191ecb062 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Components/WerewolfRuleComponent.cs @@ -0,0 +1,21 @@ +using Content.Shared.Store; +using Robust.Shared.Prototypes; + +namespace Content.Inky.Shared.Werewolf.Components; + +[RegisterComponent] +public sealed partial class WerewolfRuleComponent : Component +{ + public readonly List WerewolfMinds = new(); + + public readonly List> StoreCategories = new() + { + "WerewolfChoose", + "WerewolfDire", + "WerewolfWhite", + "WerewolfBlack" + }; + + public readonly ProtoId StoreSide = new("WerewolfSide"); + public readonly ProtoId StoreApprentice = new("WerewolfBlackApprentice"); // goida +} diff --git a/Content.Inky.Shared/Werewolf/EntityEffects/AmputateLimb.cs b/Content.Inky.Shared/Werewolf/EntityEffects/AmputateLimb.cs new file mode 100644 index 00000000000..bb40f01a3ea --- /dev/null +++ b/Content.Inky.Shared/Werewolf/EntityEffects/AmputateLimb.cs @@ -0,0 +1,56 @@ +using System.Linq; +using Content.Medical.Shared.Wounds; +using Content.Shared.Body; +using Content.Shared.EntityEffects; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Inky.Shared.Werewolf.EntityEffects; + +/// +/// Amputates a limb from an entity if it has one. +/// +public sealed partial class AmputateLimb : EntityEffectBase +{ + [DataField(required: true)] + public string LimbName { get; set; } = string.Empty; + + public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + => null; +} + +public sealed partial class AmputateLimbEffectSystem : EntityEffectSystem +{ + [Dependency] private BodySystem _body = default!; + [Dependency] private WoundSystem _wound = default!; + [Dependency] private IRobustRandom _random = default!; + + protected override void Effect(Entity ent, ref EntityEffectEvent args) // yes this is a copypaste from sharedwerewolfbasicabilitiessystem kill me todo werewolf + { + if (!TryComp(ent, out var body)) + return; + + var targetLimb = args.Effect.LimbName; + + var allOrgans = _body.GetOrgans((ent, body)); + var limbs = allOrgans + .Where(organ => + { + var category = _body.GetCategory(new Entity(organ.Owner, organ.Comp)); + return category == targetLimb; + }) + .ToList(); + + if (limbs.Count <= 0) + return; + + var pick = _random.Next(limbs.Count); // in case if someone has two or more of this bodypart, remove a random one + var picked = limbs[pick]; + + if (!TryComp(picked.Owner, out var wound) + || !wound.ParentWoundable.HasValue) + return; + + _wound.AmputateWoundableSafely(wound.ParentWoundable.Value, picked.Owner, wound); + } +} diff --git a/Content.Inky.Shared/Werewolf/EntityEffects/ThrowDirection.cs b/Content.Inky.Shared/Werewolf/EntityEffects/ThrowDirection.cs new file mode 100644 index 00000000000..2e37140d22c --- /dev/null +++ b/Content.Inky.Shared/Werewolf/EntityEffects/ThrowDirection.cs @@ -0,0 +1,51 @@ +using Content.Shared.EntityEffects; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Throwing; +using Robust.Shared.Prototypes; + +namespace Content.Inky.Shared.Werewolf.EntityEffects; + +/// +/// Throws the target entity away related to the user into the oposite dirrection +/// +public sealed partial class ThrowDirection : EntityEffectBase +{ + [DataField] + public float Speed = 10f; + + [DataField] + public bool Predicted = true; + + [DataField] + public bool StopPull = true; + + public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + => null; +} + +public sealed partial class ThrowDirectionEffectSystem : EntityEffectSystem +{ + [Dependency] private ThrowingSystem _JOHNCENA = default!; + [Dependency] private PullingSystem _pulling = default!; + + protected override void Effect(Entity ent, ref EntityEffectEvent args) + { + if (args.User is null) + return; + + var userPos = Transform(args.User.Value).WorldPosition; + var victimPos = Transform(ent).WorldPosition; + + var target = (victimPos - userPos).Normalized(); + + var effect = args.Effect; + _JOHNCENA.TryThrow(ent, + target, + baseThrowSpeed: effect.Speed, + user: args.User, + predicted: effect.Predicted); + if (effect.StopPull && TryComp(ent, out var pullable)) + _pulling.TryStopPull(ent, pullable); + } +} diff --git a/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.Black.cs b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.Black.cs new file mode 100644 index 00000000000..d9e4d5953aa --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.Black.cs @@ -0,0 +1,180 @@ +using Content.Inky.Shared.Werewolf.Components; +using Content.Shared.Body; +using Content.Shared.DoAfter; +using Content.Shared.Mind; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Popups; +using Content.Shared.Store; +using Content.Shared.Store.Components; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Inky.Shared.Werewolf.Systems; + +public sealed partial class SharedWerewolfAbilitiesSystem +{ + private const string WerewolfTransformBlack = "WerewolfTransformBlack"; + private static readonly ProtoId WerewolfBlackListing = "WerewolfBlack"; + + public void InitializeBlack() + { + SubscribeLocalEvent(TryBite); + SubscribeLocalEvent(DoBite); + + SubscribeLocalEvent(OnBequeath); + SubscribeLocalEvent(OnLeaderDied); + } + + private void TryBite(EntityUid uid, WerewolfAbilitiesComponent comp, EventWerewolfBlackBite args) + { + if (TryComp(args.Target, out var mobState) && mobState.CurrentState == MobState.Dead) + { + _popup.PopupEntity(Loc.GetString("werewolf-bite-fail-state"), uid, uid, PopupType.Large); + return; + } + if (TryComp(args.Target, out var bit)) + { + _popup.PopupEntity(Loc.GetString("werewolf-bite-fail-bit"), uid, uid, PopupType.Large); + return; + } + if (TryComp(args.Target, out var immune)) // todo werewolf use for chaplain and holy stuff + { + _popup.PopupEntity(Loc.GetString("werewolf-bite-fail-immune"), uid, uid, PopupType.Large); + return; + } + if (HasComp(args.Target)) + { + _popup.PopupPredicted(Loc.GetString("werewolf-devour-fail-werewolf"), uid, uid); // no to eating each other + return; + } + + _popup.PopupEntity(Loc.GetString("werewolf-bite-start", ("user", uid), ("target", args.Target)), uid, uid, PopupType.LargeCaution); // todo locale + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(1), new WerewolfBlackBiteDoAfterEvent(), uid, args.Target) + { + DistanceThreshold = 1.5f, + BreakOnDamage = true, + BreakOnMove = true, + BreakOnWeightlessMove = true, + AttemptFrequency = AttemptFrequency.StartAndEnd + }); + + args.Handled = true; + } + + private void DoBite(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfBlackBiteDoAfterEvent args) + { + if (args.Cancelled || args.Target == null + || HasComp(args.Target) + || !TryComp(args.Target, out var body)) + return; + + SpillBloodPercentage(args.Target.Value, 30); // todo werewolf unhardcode + args.Handled = true; + + var targetComp = EnsureComp(args.Target.Value); + + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp)) + return; + + mindComp.Currency += comp.AmountDevour; + mindComp.BittenPeople.Add(args.Target.Value); + targetComp.BittenBy = mindComp; + + targetComp.Infected = _gambling.Prob(0.5f); // todo werewolf unhardcode the 50% chance? + + _audio.PlayPvs(comp.RipSound, uid); + } + + private void OnBequeath(EntityUid uid, WerewolfAbilitiesComponent comp, EventWerewolfBequeath args) + { + if (!_mind.TryGetMind(uid, out var leadMind, out _) + || !TryComp(leadMind, out var leadMindComp)) + return; + + if (!_mind.TryGetMind(args.Target, out var targetMindId, out _)) + return; + + if (!leadMindComp.PackMembers.Contains(targetMindId)) + { + _popup.PopupEntity(Loc.GetString("werewolf-bequeath-fail-not-pack"), uid, uid, PopupType.Large); + return; + } + + var qthead = EnsureComp(targetMindId); + qthead.OriginalLeader = leadMindComp; + + _popup.PopupEntity(Loc.GetString("werewolf-bequeath-success"), uid, uid, PopupType.Medium); + args.Handled = true; + + RaiseLocalEvent(uid, new WerewolfActionRemoveEvent(args.Action)); // one time use FUCK THEM PROPER ECS INFRASTRUCTURE NO comp.OneTimeUse + } + + private void OnLeaderDied(EntityUid uid, WerewolfAbilitiesComponent comp, MobStateChangedEvent args) + { + if (args.NewMobState != MobState.Dead) + return; + + if (!_mind.TryGetMind(uid, out var leaderMindId, out _) + || !TryComp(leaderMindId, out var leaderMindComp)) + return; + + var eqe = EntityQueryEnumerator(); + while (eqe.MoveNext(out var mindEnt, out var quComp)) + { + if (quComp.OriginalLeader != leaderMindComp) + continue; + + if (!TryComp(mindEnt, out var mindComponent) + || mindComponent.OwnedEntity is not { } quEnt) + continue; + + if (!TryComp(quEnt, out var wComp)) + continue; + + wComp.CurrentMutation = WerewolfTransformBlack; + Dirty(quEnt, wComp); + + if (TryComp(mindEnt, out var werewolfMind)) + { + werewolfMind.CurrentMutation = WerewolfTransformBlack; + werewolfMind.StoreCategories.Add(quComp.Store); + } + + var store = EnsureComp(quEnt); + store.Categories.Add(quComp.Store); + + RemComp(mindEnt); + + _popup.PopupEntity(Loc.GetString("werewolf-bequeath-triggered"), quEnt, quEnt, PopupType.LargeCaution); + } + } + + #region infection + public void UpdateBlack(float frameTime) // not frametime but who carews + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var bit)) + { + if (!bit.Infected) + continue; + + bit.Accumulator += frameTime; + + if (bit.Accumulator < bit.LycTimer) + continue; + + RemComp(uid); + + if (bit.BittenBy != null && _mind.TryGetMind(uid, out var mind, out _)) + bit.BittenBy.PackMembers.Add(mind); + + var ev = new WerewolfInfectionFinishedEvent(uid); + RaiseLocalEvent(ref ev); + } + } + + #endregion +} diff --git a/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.Dire.cs b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.Dire.cs new file mode 100644 index 00000000000..2696d60c550 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.Dire.cs @@ -0,0 +1,66 @@ +using Content.Inky.Shared.Werewolf.Components; +using Content.Shared.Body.Components; +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Popups; + +namespace Content.Inky.Shared.Werewolf.Systems; + +public partial class SharedWerewolfAbilitiesSystem +{ + public void InitializeDire() + { + SubscribeLocalEvent(TryBite); + SubscribeLocalEvent(DoBite); + } + + private void TryBite(EntityUid uid, WerewolfAbilitiesComponent component, EventWerewolfBleedingBite args) + { + if (TryComp(args.Target, out var mobState) && mobState.CurrentState == MobState.Dead) // to prevent wolves from biting corpses for heals and whatnot + { + _popup.PopupEntity(Loc.GetString("werewolf-bite-fail-state"), uid, uid, PopupType.Large); + return; + } + // also intentionally no check for WerewolfAbilitiesComponent so you can actually fight other werewolf for health and shit + + _popup.PopupEntity(Loc.GetString("werewolf-bite-start", ("user", uid), ("target", args.Target)), uid, uid, PopupType.LargeCaution); // todo locale + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(1), new WerewolfBleedingBiteDoAfterEvent(), uid, args.Target) + { + DistanceThreshold = 1.5f, + BreakOnDamage = true, + BreakOnMove = true, + BreakOnWeightlessMove = true, + AttemptFrequency = AttemptFrequency.StartAndEnd + }); + + args.Handled = true; + } + + private void DoBite(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfBleedingBiteDoAfterEvent args) + { + if (args.Cancelled || args.Target == null) + return; + + SpillBloodPercentage(args.Target.Value, 30); // todo werewolf unhardcode + TryRegen(uid, comp, new EventWerewolfRegen()); // goida + + args.Handled = true; + } + + private void SpillBloodPercentage(EntityUid uid, int percentage) // if you make the number be negative or above 100 i will be very sad. + { + if (!TryComp(uid, out var stream)) + return; + + if (!_solution.ResolveSolution(uid, stream.BloodSolutionName, ref stream.BloodSolution, out var solution)) + return; + + var blood = _solution.SplitSolution(stream.BloodSolution.Value, solution.Volume * (percentage / 100f)); + + if (blood.Volume > FixedPoint2.Zero) + _puddle.TrySpillAt(uid, blood, out _); + } +} diff --git a/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.White.cs b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.White.cs new file mode 100644 index 00000000000..c131ca9b80d --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.White.cs @@ -0,0 +1,228 @@ +using System.Linq; +using System.Numerics; +using Content.Inky.Shared.Werewolf.Components; +using Content.Shared.Localizations; +using Content.Shared.Mind.Components; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Popups; +using Content.Trauma.Common.CollectiveMind; + +namespace Content.Inky.Shared.Werewolf.Systems; + +public sealed partial class SharedWerewolfAbilitiesSystem +{ + private const float MarkNotificationInterval = 15f; // in seconds todo werewolf unhardcode? + public void InitializeWhite() + { + SubscribeLocalEvent(TryTransfurmWhite); + SubscribeLocalEvent(OnPosQuery); + SubscribeLocalEvent(OnCollectiveMindBuy); + SubscribeLocalEvent(OnRevelation); + } + + private void TryTransfurmWhite(EntityUid uid, WerewolfAbilitiesComponent comp, TransfurmWhiteEvent args) + { + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp)) + return; + + if (mindComp.Accumulator < mindComp.TransfurmOnCommandDelay) + { + args.Handled = true; + return; + } + + var victimMindUid = Calc(uid, comp, args); + + RaiseLocalEvent(uid, new TransfurmEvent()); + + if (mindComp.CurrentMarkedVictim != null) + { + var oldVictimEntity = GetMindShit(mindComp.CurrentMarkedVictim.Value); + if (oldVictimEntity != null) + RemComp(oldVictimEntity.Value); + mindComp.CurrentMarkedVictim = null; + } + + if (victimMindUid != null) + mindComp.CurrentMarkedVictim = victimMindUid; + + args.Handled = true; + } + + private void OnPosQuery(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfPositionQueryEvent args) + { + var pos = Transform(uid).MapPosition; + args.Positions[uid] = pos.Position; + } + + /// + /// Calculates the closest werewolf to the hunter wolf (the mind) + /// + private EntityUid? Calc(EntityUid uid, WerewolfAbilitiesComponent comp, TransfurmWhiteEvent args) + { + var entMapCoords = _transform.GetMapCoordinates(uid); + EntityUid? closestUid = null; + EntityUid? closestMindId = null; + var minDistanceSq = args.Radius * args.Radius; + + if (_mind.TryGetMind(uid, out var initMind, out _) && TryComp(initMind, out var initMindComp)) + initMindComp.MarkImmune = true; // :trol: + + var eqe = EntityQueryEnumerator(); + while (eqe.MoveNext(out var otherUid, out var mindContainer)) + { + if (mindContainer.Mind is not { } mind + || !TryComp(mind, out var otherMind)) + continue; + + if (otherUid == uid || otherMind.MarkImmune) + continue; + + var otherMapCoords = _transform.GetMapCoordinates(otherUid); + + if (otherMapCoords.MapId != entMapCoords.MapId) + continue; + + var distSq = Vector2.DistanceSquared(entMapCoords.Position, otherMapCoords.Position); + if (distSq < minDistanceSq) + { + minDistanceSq = distSq; + closestUid = otherUid; + closestMindId = mind; // fuck! + } + } + + if (closestUid == null) + return null; + + var mark = EnsureComp(closestUid.Value); + mark.MarkedBy = uid; + + _popup.PopupEntity(Loc.GetString("werewolf-marked-popup"), + closestUid.Value, + closestUid.Value, + PopupType.LargeCaution); + + return closestMindId; + } + + public void UpdateMark(float frameTime) // its not frameTime but who cares lmao + { + var eqe = EntityQueryEnumerator(); + while (eqe.MoveNext(out var uid, out var comp)) + { + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp)) + continue; + // partially copied from heretic living heart todo werewolf replace with the vampire thingy when thats around bcuz this right here is a horrible piece of crap + if (mindComp.CurrentMarkedVictim == null) + continue; + + var victimEnt = GetMindShit(mindComp.CurrentMarkedVictim.Value); + if (victimEnt == null) + { + mindComp.CurrentMarkedVictim = null; + continue; + } + + var victim = victimEnt.Value; + + if (TryComp(uid, out var hunterState) && hunterState.CurrentState == MobState.Dead) + { + if (TryComp(victim, out _)) + RemComp(victim); + mindComp.CurrentMarkedVictim = null; + continue; + } + if (TryComp(victim, out var victimState) && victimState.CurrentState == MobState.Dead) + { + RemComp(victim); + mindComp.CurrentMarkedVictim = null; + continue; + } + + mindComp.AccumulatorPopup -= frameTime; + if (mindComp.AccumulatorPopup > 0) + continue; + + if (victimState == null) + return; + if (mindComp.AccumulatorPopup <= 0) + { + mindComp.AccumulatorPopup = MarkNotificationInterval; + string loc; + + var state = victimState.CurrentState; + var locstate = state.ToString().ToLower(); + + var ourMapCoords = _transform.GetMapCoordinates(uid); + var targetMapCoords = _transform.GetMapCoordinates(victim); + + if (_map.IsPaused(targetMapCoords.MapId)) + loc = Loc.GetString("heretic-livingheart-unknown"); // todo werewolf + else if (targetMapCoords.MapId != ourMapCoords.MapId) + loc = Loc.GetString("heretic-livingheart-faraway", ("state", locstate)); + else + { + var targetStation = _station.GetOwningStation(victim); + var ownStation = _station.GetOwningStation(uid); + + var isOnStation = targetStation != null && targetStation == ownStation; + + var ang = Angle.Zero; + if (_map.TryFindGridAt(_transform.GetMapCoordinates(Transform(uid)), out var grid, out var _)) + ang = Transform(grid).LocalRotation; + + var vector = targetMapCoords.Position - ourMapCoords.Position; + var direction = (vector.ToWorldAngle() - ang).GetDir(); + + var locdir = ContentLocalizationManager.FormatDirection(direction).ToLower(); + + loc = Loc.GetString(isOnStation ? "heretic-livingheart-onstation" : "heretic-livingheart-offstation", + ("state", locstate), + ("direction", locdir)); + } + + _popup.PopupEntity(loc, uid, uid, PopupType.Medium); + } + } + } + + private void OnCollectiveMindBuy(EntityUid uid, + WerewolfAbilitiesComponent comp, + WerewolfAddCollectivemind args) + { + EnsureComp(uid, out var m); + m.Channels.Add(args.NewChannel); + if (args.Popup != null) + _popup.PopupEntity(Loc.GetString(args.Popup), uid, uid, PopupType.Medium); + } + + private void OnRevelation(EntityUid uid, + WerewolfAbilitiesComponent comp, + WerewolfRevelationEvent args) + { + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp)) + return; + + RaiseLocalEvent(uid, new TransfurmWhiteEvent()); + mindComp.BlockTransfurm = true; + } + + + private EntityUid? GetMindShit(EntityUid targetMind) + { + var eqe = EntityQueryEnumerator(); + while (eqe.MoveNext(out var entityUid, out var mindContainer)) + { + if (mindContainer.Mind == targetMind) + return entityUid; + } + return null; + } + + +} diff --git a/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.cs b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.cs new file mode 100644 index 00000000000..4513970f04e --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfAbilitiesSystem.cs @@ -0,0 +1,286 @@ +using System.Numerics; +using Content.Inky.Shared.Werewolf.Components; +using Content.Shared.Actions; +using Content.Shared.Camera; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Content.Shared.Fluids; +using Content.Shared.Mind; +using Content.Shared.Popups; +using Content.Shared.Station; +using Content.Shared.Stunnable; +using Content.Shared.Tag; +using Content.Shared.Throwing; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Map; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Inky.Shared.Werewolf.Systems; + +public sealed partial class SharedWerewolfAbilitiesSystem : EntitySystem +{ + [Dependency] private SharedAudioSystem _audio = default!; + [Dependency] private SharedMindSystem _mind = default!; + [Dependency] private ActionContainerSystem _actionCon = default!; + [Dependency] private SharedActionsSystem _actions = default!; + [Dependency] private ISharedPlayerManager _player = default!; + [Dependency] private SharedCameraRecoilSystem _recoil = default!; + [Dependency] private EntityLookupSystem _entityLookup = default!; + [Dependency] private SharedStunSystem _stun = default!; + [Dependency] private SharedPopupSystem _popup = default!; + [Dependency] private TagSystem _tag = default!; + + [Dependency] private ThrownItemSystem _throwingItem = default!; + [Dependency] private ThrowingSystem _throwing = default!; + [Dependency] private SharedContainerSystem _container = default!; + [Dependency] private SharedDoAfterSystem _doAfter = default!; + [Dependency] private SharedSolutionContainerSystem _solution = default!; + [Dependency] private SharedPuddleSystem _puddle = default!; + [Dependency] private SharedTransformSystem _transform = default!; + [Dependency] private SharedStationSystem _station = default!; + [Dependency] private SharedMapSystem _map = default!; + [Dependency] private IRobustRandom _gambling = default!; + + private float _updateTimer = 0f; + /* + * transfurmevent triggers polymorph shitcode that alters WerewolfAbilitiesComponent + * which makes the eqe shit itself and crash the server + * so we are collecting ents that need to transform to proccess them after + */ + private List _transfurmQueue = new(); + + public override void Initialize() + { + SubscribeLocalEvent(DoHowl); + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnUpgradeAbility); + + SubscribeLocalEvent(OnAmbush); + SubscribeLocalEvent(OnHit); + + SubscribeLocalEvent(TryRegen); + + InitializeDire(); + InitializeWhite(); + InitializeBlack(); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + _updateTimer += frameTime; + if (_updateTimer < 0.5f) + return; + + var timePassed = _updateTimer; + _updateTimer = 0f; + + _transfurmQueue.Clear(); + + var eqe = EntityQueryEnumerator(); + while (eqe.MoveNext(out var uid, out var comp)) + { + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp) + || mindComp.BlockTransfurm) + continue; + + mindComp.Accumulator += timePassed; + + if (mindComp.Accumulator >= mindComp.TransfurmWarnDelay && !mindComp.HasWarned) + { + _popup.PopupEntity(Loc.GetString(mindComp.TransfurmPopup), uid, uid, PopupType.LargeCaution); + mindComp.HasWarned = true; + } + + if (mindComp.Accumulator >= mindComp.TransfurmOnCommandDelay && !mindComp.TransfurmReady) + { + _popup.PopupEntity(Loc.GetString(mindComp.TransfurmReadyPopup), uid, uid, PopupType.Medium); + mindComp.TransfurmReady = true; + } + + if (mindComp.Accumulator >= mindComp.TransfurmCycle) + { + mindComp.TransfurmReady = false; + mindComp.HasWarned = false; + _transfurmQueue.Add(uid); + } + } + + foreach (var uid in _transfurmQueue) + RaiseLocalEvent(uid, new TransfurmEvent()); + + UpdateMark(timePassed); + UpdateBlack(timePassed); // if there would ever be an infection cure for this, use same shit as _transfurmQueue because it'll probably make eqe shit itself too + } + + private const string DogTag = "VulpEmotes"; + public void OnStartup(EntityUid uid, WerewolfAbilitiesComponent comp, ref ComponentStartup args) + { + if (_mind.TryGetMind(uid, out var mindId, out _) + && TryComp(mindId, out var mindComp) + && mindComp.CurrentMutation is { } currentMutation) + { + comp.CurrentMutation = currentMutation; + return; + } + + if (_tag.HasTag(uid, DogTag)) + { + comp.CurrentMutation = "WerewolfTransformWerehuman"; // TODO WEREWOLF unshit CurrentMutation to not use fucking string??? are you fucking retarded????? + return; + } + comp.CurrentMutation = "WerewolfTransformBasic"; // goida + } + + # region action handlers + private void DoHowl(EntityUid uid, WerewolfAbilitiesComponent comp, ref HowlEvent args) //kill me for copying changeling system please + { + _audio.PlayPredicted(comp.ShriekSound, uid, uid); + + var center = Transform(uid).MapPosition; + var gamers = Filter.Empty(); + gamers.AddInRange(center, args.ShriekPower, _player, EntityManager); + + foreach (var gamer in gamers.Recipients) + { + if (gamer.AttachedEntity == null) + continue; + + var pos = Transform(gamer.AttachedEntity!.Value).WorldPosition; + var delta = center.Position - pos; + + if (delta.EqualsApprox(Vector2.Zero)) + delta = new(.01f, 0); + + _recoil.KickCamera(uid, -delta.Normalized()); + foreach (var entity in _entityLookup.GetEntitiesInRange(uid, args.ShriekPower)) + { + _stun.TryUpdateStunDuration(entity, TimeSpan.FromSeconds(args.StunDuration)); + _stun.TryKnockdown(entity, TimeSpan.FromSeconds(args.StunDuration), true); + } + } + + if (args.ForceTransfurm || args.HealNearby) + { + List? pack = null; + if (args.PackOnly) + { + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp)) + return; + + pack = mindComp.PackMembers; + } + + foreach (var wolf in _entityLookup.GetEntitiesInRange(uid, args.ShriekPower)) + { + if (!HasComp(wolf)) + continue; + + if (pack != null) + { + if (!_mind.TryGetMind(wolf, out var mind, out _) + || !pack.Contains(mind)) + continue; + } + + if (args.ForceTransfurm) + RaiseLocalEvent(wolf, new TransfurmEvent(true)); + + if (args.HealNearby) + RaiseLocalEvent(wolf, new EventWerewolfRegen()); + } + } + _audio.PlayGlobal(comp.DistantSound, uid, AudioParams.Default.WithVolume(-30f)); // when you howl, everyone on the station hears a quiet distant howl, which breaks the metashield for the chaplain, "allegedly" todo uncomment when better sound is found + args.Handled = true; + } + private void OnAmbush(EntityUid uid, WerewolfAbilitiesComponent comp, WerewolfAmbushActionEvent args) // partially taken from xenos jump + { + if (args.Handled + || _container.IsEntityInContainer(uid)) + return; + + _throwing.TryThrow(uid, args.Target, args.JumpSpeed, uid, 10F); + // todo PlayPVS + args.Handled = true; + } + + private void OnHit(EntityUid uid, WerewolfAbilitiesComponent comp, ThrowDoHitEvent args) + { + // if (args.Handled) + // return; + + _throwingItem.StopThrow(uid, args.Component); + + if (Transform(args.Target).Anchored) + _stun.TryUpdateParalyzeDuration(uid, TimeSpan.FromSeconds(1)); + else + _stun.TryKnockdown(args.Target, TimeSpan.FromSeconds(1), true); + + // args.Handled = true; + } + #endregion + + #region store related shit + /// + /// Deletes and replaces the args.OldActionId with the args.NewActionId, also adding it to the mind + /// + private void OnUpgradeAbility(EntityUid uid, WerewolfAbilitiesComponent comp, EventWerewolfUpgradeAbility args) + { + if (!_mind.TryGetMind(uid, out var mindId, out _) + || !TryComp(mindId, out var mindComp)) + return; + + // update the mind to have those new actions + if (args.OldActionId != null) // holy fucking kill myself + { + mindComp.UnlockedActions.Remove(args.OldActionId); + if (_actions.TryGetActionById(mindId, args.OldActionId, out var oldAction)) + _actionCon.RemoveAction(oldAction.Value.AsNullable()); + else if (_actions.TryGetActionById(uid, args.OldActionId, out var oldAttachedAction)) + _actions.RemoveAction(uid, oldAttachedAction.Value.AsNullable()); + } + + if (!mindComp.UnlockedActions.Contains(args.NewActionId)) + mindComp.UnlockedActions.Add(args.NewActionId); + + var action = _actionCon.AddAction(mindId, args.NewActionId); + if (action != null) + _actions.GrantContainedAction(uid, mindId, action.Value); + + _popup.PopupEntity(Loc.GetString("werewolf-ability-upgraded"), uid, uid); + args.Handled = true; + } + #endregion + + public bool TryInjectReagents(EntityUid uid, Dictionary reagents) + { + var solution = new Solution(); + foreach (var (reagentId, quantity) in reagents) + solution.AddReagent(reagentId, quantity); + + if (!_solution.TryGetInjectableSolution(uid, out var targetSolution, out _)) + return false; + + return _solution.TryAddSolution(targetSolution.Value, solution); + } + + private void TryRegen(EntityUid uid, WerewolfAbilitiesComponent comp, EventWerewolfRegen args) + { + var reagents = new Dictionary // i hate fixedpoint bru // todo werewolf unhardcode, put into a comp idk + { + ["Ichor"] = FixedPoint2.New(10), + ["TranexamicAcid"] = FixedPoint2.New(5) + }; + + if (TryInjectReagents(uid, reagents)) + _popup.PopupPredicted(Loc.GetString("werewolf-action-regen-success"), uid, uid); + args.Handled = true; + } +} diff --git a/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfActionSystem.cs b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfActionSystem.cs new file mode 100644 index 00000000000..8fe3687674b --- /dev/null +++ b/Content.Inky.Shared/Werewolf/Systems/SharedWerewolfActionSystem.cs @@ -0,0 +1,54 @@ +using Content.Inky.Shared.Werewolf.Components; +using Content.Shared.Actions.Events; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Popups; + +namespace Content.Inky.Shared.Werewolf.Systems; + +public sealed partial class SharedWerewolfActionSystem : EntitySystem +{ + [Dependency] private SharedPopupSystem _popup = default!; + [Dependency] private HungerSystem _hunger = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnActionAttempt); + } + + private void OnActionAttempt(Entity ent, ref ActionAttemptEvent args) + { + if (args.Cancelled) + return; + + var user = args.User; + var comp = ent.Comp; + + if (comp.RequireTransfurmed) + { + if (!TryComp(user, out var wolf) || !wolf.Transfurmed) + { + _popup.PopupClient(Loc.GetString(comp.NotTransfurmedPopup), user, user); + args.Cancelled = true; + return; + } + } + + if (comp.HungerCost > 0) + { + if (!TryComp(user, out var hunger)) + return; + + if (_hunger.GetHunger(hunger) < comp.HungerCost) + { + _popup.PopupClient(Loc.GetString(comp.NoHungerPopup), user, user); + args.Cancelled = true; + return; + } + } + + _hunger.ModifyHunger(user, -comp.HungerCost); + } +} diff --git a/Content.Inky.Shared/Werewolf/WerewolfEvents.cs b/Content.Inky.Shared/Werewolf/WerewolfEvents.cs new file mode 100644 index 00000000000..50143eca5f7 --- /dev/null +++ b/Content.Inky.Shared/Werewolf/WerewolfEvents.cs @@ -0,0 +1,113 @@ +using System.Numerics; +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Inky.Shared.Werewolf; + +public sealed partial class HowlEvent : InstantActionEvent +{ + [DataField] public float ShriekPower = 2.5f; + [DataField] public int StunDuration = 1; + + /// + /// Transforms every werewolf in radius if true + /// + [DataField] public bool ForceTransfurm; // fucking goida bro + /// + /// Raises EventWerewolfRegen on every werewolf in radius if true + /// + [DataField] public bool HealNearby; + + /// + /// whether or not should healing & transforms work only on your pack + /// + [DataField] public bool PackOnly = true; +} + +public sealed partial class TransfurmEvent : InstantActionEvent +{ + [DataField] public bool Forced; + + public TransfurmEvent() {} + public TransfurmEvent(bool forced) + { + Forced = forced; + } +} + +public sealed partial class TransfurmWhiteEvent : InstantActionEvent +{ + /// + /// Searching radius, when any one werewolf but the entity is in that radius, they will be marked + /// + [DataField] public float Radius = 50f; +} +public sealed partial class EventWerewolfOpenStore : InstantActionEvent {} +public sealed partial class EventWerewolfDevour : EntityTargetActionEvent {} +public sealed partial class EventWerewolfGut : EntityTargetActionEvent {} +public sealed partial class EventWerewolfBleedingBite : EntityTargetActionEvent {} +public sealed partial class EventWerewolfBlackBite : EntityTargetActionEvent {} +public sealed partial class EventWerewolfChangeType : InstantActionEvent +{ + [DataField] public string WerewolfType; +} + +public sealed partial class EventWerewolfRegen : InstantActionEvent {} + +public sealed partial class WerewolfAmbushActionEvent : WorldTargetActionEvent +{ + [DataField] + public float JumpSpeed = 15f; +} + +[Serializable, NetSerializable] +public sealed partial class WerewolfDevourDoAfterEvent : SimpleDoAfterEvent { } + +[Serializable, NetSerializable] +public sealed partial class WerewolfGutDoAfterEvent : SimpleDoAfterEvent { } +[Serializable, NetSerializable] +public sealed partial class WerewolfBleedingBiteDoAfterEvent : SimpleDoAfterEvent { } +[Serializable, NetSerializable] +public sealed partial class WerewolfBlackBiteDoAfterEvent : SimpleDoAfterEvent { } + +// upgrade events idk +// event raised when any werewolf ability is upgraded +// yes this is horrible and probably would be better to replace this with ProductUpgradeId but its kinda shit +public sealed partial class EventWerewolfUpgradeAbility : InstantActionEvent +{ + /// + /// The prototype ID of the action to be replaced + /// + [DataField] + public string? OldActionId; + + /// + /// The prototype ID of the new upgraded action + /// + [DataField] + public string NewActionId; +} + +public sealed class WerewolfPositionQueryEvent : EntityEventArgs +{ + public Dictionary Positions { get; } = new(); +} + +public sealed partial class WerewolfAddCollectivemind : InstantActionEvent +{ + [DataField] public string NewChannel = "LunarMind"; + [DataField] public string? Popup; +} + +public sealed partial class WerewolfRevelationEvent : InstantActionEvent; +public sealed partial class WerewolfBlackCallEvent : InstantActionEvent; +[ByRefEvent] +public readonly record struct WerewolfInfectionFinishedEvent(EntityUid Entity); +public sealed partial class WerewolfBeckonEvent : InstantActionEvent; +public sealed partial class EventWerewolfBequeath : EntityTargetActionEvent {} +public sealed class WerewolfActionRemoveEvent : EntityEventArgs +{ + public readonly EntityUid ActionEnt; + public WerewolfActionRemoveEvent(EntityUid actionEnt) => ActionEnt = actionEnt; +} diff --git a/Content.Shared/CombatMode/CombatModeComponent.cs b/Content.Shared/CombatMode/CombatModeComponent.cs index 124a682d5c7..78c7a62288d 100644 --- a/Content.Shared/CombatMode/CombatModeComponent.cs +++ b/Content.Shared/CombatMode/CombatModeComponent.cs @@ -13,7 +13,7 @@ namespace Content.Shared.CombatMode /// using *everything* as a weapon. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] - [Access(typeof(SharedCombatModeSystem))] + // [Access(typeof(SharedCombatModeSystem))] // inky edit - go fucking kill your self oh my fucking GOD public sealed partial class CombatModeComponent : Component { #region Disarm diff --git a/Content.Trauma.Shared/Knowledge/Systems/SharedKnowledgeSystem.Inky.cs b/Content.Trauma.Shared/Knowledge/Systems/SharedKnowledgeSystem.Inky.cs new file mode 100644 index 00000000000..c855344d69b --- /dev/null +++ b/Content.Trauma.Shared/Knowledge/Systems/SharedKnowledgeSystem.Inky.cs @@ -0,0 +1,30 @@ +using Content.Inky.Common.Events.Werewolf; + +namespace Content.Trauma.Shared.Knowledge.Systems; + +public abstract partial class SharedKnowledgeSystem +{ + private void InitializeInky() + { + SubscribeLocalEvent(OnSelectFirstMartialArt); + } + /// + /// selects the first martial art from the known martial arts + /// + private void OnSelectFirstMartialArt(SelectFirstMartialArtEvent args) + { + if (GetContainer(args.Entity) is not { } container + || container.Comp.ActiveMartialArt != null) + return; + + foreach (var knowledgeUid in container.Comp.KnowledgeDict.Values) + { + if (!_artQuery.HasComp(knowledgeUid) + || !Exists(knowledgeUid)) + continue; + + ChangeMartialArts(container, args.Entity, knowledgeUid); + return; + } + } +} diff --git a/Content.Trauma.Shared/Knowledge/Systems/SharedKnowledgeSystem.cs b/Content.Trauma.Shared/Knowledge/Systems/SharedKnowledgeSystem.cs index b56afbd8eea..a74cb651627 100644 --- a/Content.Trauma.Shared/Knowledge/Systems/SharedKnowledgeSystem.cs +++ b/Content.Trauma.Shared/Knowledge/Systems/SharedKnowledgeSystem.cs @@ -67,6 +67,10 @@ public override void Initialize() Subs.CVar(_cfg, TraumaCVars.SkillGain, x => _skillGain = x, true); LoadSkillPrototypes(); + + // inky + InitializeInky(); + // /inky } public override void Update(float frameTime) diff --git a/Resources/Audio/_Inky/Antag/Werewolf/attributions.yml b/Resources/Audio/_Inky/Antag/Werewolf/attributions.yml new file mode 100644 index 00000000000..e3a95a28732 --- /dev/null +++ b/Resources/Audio/_Inky/Antag/Werewolf/attributions.yml @@ -0,0 +1,9 @@ +- files: ["werewolf_start.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Made by @LuciferEOS" + source: "https://github.com/LuciferEOS" + +- files: ["howl.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from https://pixabay.com/sound-effects/nature-wolf-howl-140235/" + source: "https://pixabay.com/sound-effects/nature-wolf-howl-140235/" diff --git a/Resources/Audio/_Inky/Antag/Werewolf/howl.ogg b/Resources/Audio/_Inky/Antag/Werewolf/howl.ogg new file mode 100644 index 00000000000..e2a2c6736f2 Binary files /dev/null and b/Resources/Audio/_Inky/Antag/Werewolf/howl.ogg differ diff --git a/Resources/Audio/_Inky/Antag/Werewolf/werewolf_start.ogg b/Resources/Audio/_Inky/Antag/Werewolf/werewolf_start.ogg new file mode 100644 index 00000000000..da783f88b36 Binary files /dev/null and b/Resources/Audio/_Inky/Antag/Werewolf/werewolf_start.ogg differ diff --git a/Resources/Locale/en-US/_Inky/Werewolf/werewolf.ftl b/Resources/Locale/en-US/_Inky/Werewolf/werewolf.ftl new file mode 100644 index 00000000000..2ea93df81a6 --- /dev/null +++ b/Resources/Locale/en-US/_Inky/Werewolf/werewolf.ftl @@ -0,0 +1,121 @@ +# im too fucking lazy to make 65 new files for each thing so the most that you will get is path-specific ftl files and one ginormous shared one +collective-mind-lunarmind = LunarMind +werewolf-beckon-message = {$name} beckons the pack to {$location}. + +role-subtype-werewolf = Werewolf +roles-antag-werewolf-name = Werewolf +roles-antag-werewolf-desc = Whether by infection or hereditary genes, you’ve been given the curse and/or gift of Lycanthropy! Aren’t you special? +werewolf-role-greeting = I am the Werewolf. Unbeknownst to my employers, I have been cursed with Lycanthropy. I must ensure my survival on this station, and keep well fed. Secrecy is my weapon, I must ensure that nobody finds out my real identity. + +werewolf-action-fail-hunger = You are too hungry to do that right now. +werewolf-action-fail-transfurmed = You cant use it while being in inferior form. + +werewolf-transfurm-block = Something is blocking you transforming... +werewolf-transfurm-cooldown = We are not yet ready to transform. +werewolf-mutation-changed = You feel yourself shift. +werewolf-devour-fail-werewolf = It smells a wolf... You cant devour it. +werewolf-devour-start = {$user} bites into the {$target} arm! +werewolf-gut-start = {$user} guts into {$target} torso! +werewolf-gut-no-organs-left = There is nothing to eat. +werewolf-gut-success = {$user} eats an organ of {$target}! +werewolf-transfurm-warn = Your body hurts, you are about to transform. +werewolf-transfurm-ready = You feel ready to transform. +werewolf-bite-fail-state = This isnt something we can devour. +werewolf-bite-fail-bit = It smells of wolf... It has been bit before. +werewolf-bite-fail-immune = Something is blocking you from doing that. +werewolf-bite-start = {$user} starts to bite into {$target}! +werewolf-bequeath-fail-not-pack = This is not someone of our pack. +werewolf-bequeath-success = Bequeath successful. +werewolf-bequeath-triggered = You feel that the leader has died. You take over his place. +werewolf-ability-upgraded = You feel stronger. +werewolf-action-regen-success = You feel your body recovering. +werewolf-gut-fail-mind = You are above to eat this. + +werewolf-black-call-success = Reliquish your Humanity, and give in to your instincts, it is time to show the station your true identity. +werewolf-black-call-fail-amount = You need more people in your pack to do that! + + +# i do not fucking care + +store-currency-display-fury = Fury +werewolf-store-choose = Choose +werewolf-store-dire = Direwolf +werewolf-store-white = White wolf +werewolf-store-black = Black wolf +werewolf-store-black-apprentice = Pack +werewolf-store-side = Side abilities + +# side + +werewolf-store-regen-name = Increased metabolism +werewolf-store-regen-desc = Increase your metabolism, allowing you to regenerate rapidly. Be aware that it makes you really hungry. Can be used in the human form. + +werewolf-store-jump-name = Ambush +werewolf-store-jump-desc = Leap at your victim, knocking them down and stunning them. + +werewolf-store-gut-name = Gut +werewolf-store-gut-desc = Tear into your victims organs, eating them, and converting them to fury. Can be used multiple times as long as the target has organs.] + +# white + +werewolf-store-choose-white-name = White wolf +werewolf-store-choose-white-desc = To some, a curse. To others, a gift. For me, opportunity. I will strike them down for their impurity. + + Your hate for werewolves is unrelenting. Allows you to access abilities to hunt those considered hunters. + Your holy claws will show the way to your true identity, and show the end to those who seek to harm you. + +werewolf-store-white-dmg-name = Silver claws +werewolf-store-white-dmg-desc = Your attacks deal added holy damage. Allowing you to seriously harm other werewolves. + +werewolf-store-white-track-name = Bloodhound +werewolf-store-white-track-desc = Track other Werewolves and mark them for Death, regardless of whether theyre transformed or not. + You deal slightly more damage to Marked werewolves. + +werewolf-store-white-lunar-name = Supperiour hearing +werewolf-store-white-lunar-desc = You now gain access to the lunarmind, allowing to hear werewolves speaking in it. + +werewolf-store-white-revelation-name = Revelation +werewolf-store-white-revelation-desc = Learn the identity of every single Werewolf alive and mark them for death. Werewolves are alerted to your presence. + Become permanently transformed after buying. Be aware that it is irreversible and you cant access the store once you have bought it. + +# dire + +werewolf-store-choose-direwolf-name = Direwolf +werewolf-store-choose-direwolf-desc = My will becomes sharpened. My body - enhanced. I will now show the world what true fear really is. + + Increases your movement speed, damage, howl and healing. Steal your victims blood and seed fear in their veins. + +werewolf-store-howl-direwolf-name = Vicious roar +werewolf-store-howl-direwolf-desc = Your howl becomes more powerful, stunning those in a bigger range for a longer time. + +werewolf-store-bite-direwolf-name = Bleeding bite +werewolf-store-bite-direwolf-desc = Sink your canines into your victim, and steal 30% of their blood. This will heal you greatly. + +# black + +werewolf-store-choose-black-name = Black wolf +werewolf-store-choose-black-desc = A gift, I have been given. Let prey become predators, and let us rise to a new era. Black as night, we hunt till the end. + + You will become slower, but will also become stronger. + Allows you to access the black path store, focusing on making crew members into other werewolf under your rule. + Expand, control, and dominate the station. + +werewolf-store-bite-black-name = Cursed bite +werewolf-store-bite-black-desc = Bite a victim, causing massive blood loss. Has a 50% chance of turning the victim into a werewolf under your rule after ten minutes. + +werewolf-store-black-lunar-name = Pack mentality +werewolf-store-black-lunar-desc = Gain access to the lunarmind, allowing you to communicate with your pack members. Be aware, that you might be heard by others. + +werewolf-store-black-order-name = Alpha order +werewolf-store-black-order-desc = Replaces the howl. Your howl now forcefully transforms werewolves of your pack, and heals those already transformed if they are near you. + +werewolf-store-black-bequeath-name = Bequeath +werewolf-store-black-bequeath-desc = Crown a werewolf from your pack to be the next leader in case of your death. Single use. + +werewolf-store-black-beckon-name = Beckon +werewolf-store-black-beckon-desc = Transmits your current location into the lunarmind. + +werewolf-black-call-name = The Call +werewolf-black-call-desc = The final stage of accepting your true form. Requires you to have minimum of 4 pack members to activate. + After use, you and everyone in your pack will become PERMANENTLY transformed, having their health doubled. + Sets the station alert to violet, single use. diff --git a/Resources/Prototypes/_Inky/CollectiveMind/lunar.yml b/Resources/Prototypes/_Inky/CollectiveMind/lunar.yml new file mode 100644 index 00000000000..78356de5f7d --- /dev/null +++ b/Resources/Prototypes/_Inky/CollectiveMind/lunar.yml @@ -0,0 +1,5 @@ +- type: collectiveMind + id: LunarMind + name: collective-mind-lunarmind + keycode: 'l' + color: "#B56328" diff --git a/Resources/Prototypes/_Inky/GameRules/roundstart.yml b/Resources/Prototypes/_Inky/GameRules/roundstart.yml new file mode 100644 index 00000000000..7ede8aa316e --- /dev/null +++ b/Resources/Prototypes/_Inky/GameRules/roundstart.yml @@ -0,0 +1,19 @@ +- type: entity + parent: BaseGameRule + id: Werewolf + components: + - type: WerewolfRule + - type: GameRule + minPlayers: 20 # Trauma, was 20 + - type: AntagPlayerEffects + effects: + - !type:NestedEffect + proto: WerewolfSkills + - type: AntagSelection + antags: + - !type:LinearAntagCount + proto: Werewolf + range: + min: 2 + max: 5 + playerRatio: 12 diff --git a/Resources/Prototypes/_Inky/Roles/mind_roles.yml b/Resources/Prototypes/_Inky/Roles/mind_roles.yml new file mode 100644 index 00000000000..370c942e66c --- /dev/null +++ b/Resources/Prototypes/_Inky/Roles/mind_roles.yml @@ -0,0 +1,12 @@ +# Werewolf +- type: entity + parent: BaseMindRoleAntag + id: MindRoleWerewolf + name: Werewolf Role + components: + - type: MindRole + antagPrototype: Werewolf + exclusiveAntag: true + roleType: SoloAntagonist + subtype: role-subtype-werewolf + - type: WerewolfRole diff --git a/Resources/Prototypes/_Inky/Werewolf/Mutations/base_werewolf.yml b/Resources/Prototypes/_Inky/Werewolf/Mutations/base_werewolf.yml new file mode 100644 index 00000000000..325b60fe284 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Mutations/base_werewolf.yml @@ -0,0 +1,179 @@ +- type: entity + parent: +# - SimpleMobBase + - MobBloodstream + - MobRespirator + - MobAtmosStandard + - MobFlammable + - BaseSimpleMob + - MobAtmosExposed + - MobCombat + - BaseMobAnimal + id: BaseWerewolf + name: wolf + description: Let's hope he doesnt bite. + abstract: true + components: + # inky + - type: ConcussionThreshold + thresholds: + 0: Sane + 5: Minor + 50: Hard + 100: Overwhelmed + healRate: 1 # lower than humans because this guy is already so fucking fast, people may ask sec fo flashbangs if the chaplain is a bitch + speedModifierThresholds: + Hard: 0.7 + # /inky + - type: Perishable + - type: Climbing + - type: NameIdentifier + group: GenericNumber + - type: SlowOnDamage + speedModifierThresholds: + 60: 0.7 + 80: 0.5 + - type: Hunger + thresholds: # only animals and rats are derived from this prototype so let's override it here and in rats' proto + Overfed: 100 + Okay: 50 + Peckish: 25 + Starving: 10 + Dead: 0 + baseDecayRate: 0.00925925925926 # it is okay for animals to eat and drink less than humans, but more frequently + - type: Thirst + thresholds: + OverHydrated: 200 + Okay: 150 + Thirsty: 100 + Parched: 50 + Dead: 0 + baseDecayRate: 0.04 + - type: StatusEffects + allowed: + - Electrocution + - TemporaryBlindness + - Pacified + - Flashed + - Adrenaline + - type: Bloodstream + bloodReferenceSolution: + reagents: + - ReagentId: Blood + Quantity: 150 + - type: MobPrice + price: 150 + - type: FloatingVisuals + - type: Sprite + sprite: _Inky/Mobs/Werewolf/Mutations/base.rsi + state: werewolf + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.45 + density: 250 # 5 times more than a hueman + mask: + - MobMask + layer: + - MobLayer + - type: Body + - type: MobState + - type: MobThresholds + thresholds: + 0: Alive + 100: Critical + 200: Dead + - type: MobStateActions + actions: + Critical: + - ActionCritSuccumb + - ActionCritFakeDeath + - ActionCritLastWords + - type: Deathgasp + - type: HealthExaminable + examinableTypes: + - Blunt + - Slash + - Piercing + - Heat + - Shock + - Cold + - Caustic + - type: MeleeWeapon + hidden: true + soundHit: + path: /Audio/Weapons/pierce.ogg + angle: 30 + animation: WeaponArcClaw + damage: + types: + Blunt: 5 + Slash: 10 + - type: Pullable + - type: Puller + - type: MovementSpeedModifier + baseSprintSpeed: 6.5 + baseWalkSpeed: 4.5 + - type: Tag + tags: + - FootstepSound + - DoorBumpOpener + - type: Insulated + - type: ThermalVision + flashDurationMultiplier: 3 # counter to werewolves and their insane speed - flashes. + pulseTime: 10 + isEquipment: false + toggleAction: PulseThermalVisionWerewolf + - type: Prying + speedModifier: 4 # cmon a fucking 11 foot wolf + pryPowered: true + - type: Hands # also dont worry you cant pick up anything + hands: + hand_right: + location: Right + sortedHands: + - hand_left # martial art requires at least 1 hand to work properly for some fuckass unknown reason + - type: NightVision + lightingColor: "#808080" + - type: Alerts # idk to show the guys hunger since it doesnt for some reason??? + - type: NpcFactionMember + factions: + - Wizard # too lazy to make a separate faction just for werewolf, its fine trust + - type: Injurable + damageContainer: Biological + - type: Damageable + damageModifierSet: WerewolfBase + - type: NoSlip + - type: Targeting + - type: Internals + +- type: entity + id: BaseWerewolfUnholy + abstract: true + components: + - type: ShouldTakeHoly + - type: Injurable + damageContainer: BiologicalMetaphysical + - type: Damageable + damageModifierSet: WerewolfBase + +- type: damageModifierSet # todo werewolf its own file + id: WerewolfBase + coefficients: + Blunt: 0.15 # GO KILL HIM WITH HOLY DAMAGE YOU BUMS + Slash: 0.15 + Piercing: 0.15 + Heat: 0.35 # mfw lasersec + Cold: 0.85 # fur + Holy: 1.0 + +- type: entityEffect + id: WerewolfSkills + effects: + - !type:GrantSkills + skills: + MartialArtLyCqc: 65 + StrengthKnowledge: 65 + AthleticsKnowledge: 65 diff --git a/Resources/Prototypes/_Inky/Werewolf/Mutations/blackwolf.yml b/Resources/Prototypes/_Inky/Werewolf/Mutations/blackwolf.yml new file mode 100644 index 00000000000..ef6cd608d8e --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Mutations/blackwolf.yml @@ -0,0 +1,14 @@ +- type: entity + id: WerewolfBlackwolf + parent: + - BaseWerewolf + - BaseWerewolfUnholy + # todo description i cant come up with anything + name: black wolf + components: + - type: Sprite + sprite: _Inky/Mobs/Werewolf/Mutations/black.rsi + state: wolf + - type: MovementSpeedModifier + baseSprintSpeed: 4.5 + baseWalkSpeed: 2.5 diff --git a/Resources/Prototypes/_Inky/Werewolf/Mutations/direwolf.yml b/Resources/Prototypes/_Inky/Werewolf/Mutations/direwolf.yml new file mode 100644 index 00000000000..7d5d224aa3c --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Mutations/direwolf.yml @@ -0,0 +1,39 @@ +- type: entity + id: WerewolfDirewolf + parent: + - BaseWerewolf + - BaseWerewolfUnholy + name: dire wolf + description: A dark, forboding feeling overtakes you. You know what will come, you can feel his intentions. + components: + - type: Sprite + sprite: _Inky/Mobs/Werewolf/Mutations/direwolf.rsi + state: direwolf + - type: Barotrauma + damage: + types: + Blunt: 0.35 + Heat: 0.1 +# - type: GrantWerewolfMoves // TODO WEREWOLF MA +# - type: LanguageSpeaker +# speaks: +# - Draconic +# understands: +# - TauCetiBasic +# - Draconic + - type: MeleeWeapon + hidden: true + soundHit: + path: /Audio/Weapons/pierce.ogg + angle: 30 + animation: WeaponArcClaw + damage: + types: + Slash: 17 + - type: MovementSpeedModifier + baseSprintSpeed: 7.5 + baseWalkSpeed: 4.5 + - type: Prying + speedModifier: 6 # a bit faster than a base wolf + pryPowered: true + diff --git a/Resources/Prototypes/_Inky/Werewolf/Mutations/werewolf.yml b/Resources/Prototypes/_Inky/Werewolf/Mutations/werewolf.yml new file mode 100644 index 00000000000..5b2f5c19a78 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Mutations/werewolf.yml @@ -0,0 +1,35 @@ +- type: entity + id: WerewolfWerewolf + parent: + - BaseWerewolf + - BaseWerewolfUnholy + components: + - type: Sprite + sprite: _Inky/Mobs/Werewolf/Mutations/base.rsi + state: werewolf + - type: Barotrauma + damage: + types: + Blunt: 0.35 + Heat: 0.1 +# - type: LanguageKnowledge # Einstein Engines - Languages +# speaks: +# - Draconic # the furry vulp language (swapped to draconic cuz trauma were based enough to get rid of it) +# understands: +# - TauCetiBasic +# - Draconic + +- type: entity + id: WerewolfWerehuman + parent: WerewolfWerewolf # should be identical to the average werewolf because memes + name: human + description: Lord have mercy. + components: + - type: Sprite + sprite: _Inky/Mobs/Werewolf/Mutations/werehuman.rsi + state: werehuman +# - type: LanguageSpeaker +# speaks: +# - TauCetiBasic +# understands: +# - TauCetiBasic diff --git a/Resources/Prototypes/_Inky/Werewolf/Mutations/whitewolf.yml b/Resources/Prototypes/_Inky/Werewolf/Mutations/whitewolf.yml new file mode 100644 index 00000000000..bba50824c6f --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Mutations/whitewolf.yml @@ -0,0 +1,49 @@ +- type: entity + id: WerewolfWhite + parent: + - BaseWerewolf + name: white wolf + categories: [ HideSpawnMenu ] # goida + description: Let's hope you haven't sinned. + components: + - type: Sprite + sprite: _Inky/Mobs/Werewolf/Mutations/white.rsi + state: wolf + - type: Barotrauma + damage: + types: + Blunt: 0.35 + Heat: 0.1 + # - type: GrantWerewolfMoves // TODO WEREWOLF MA + # - type: LanguageSpeaker + # speaks: + # - Draconic + # understands: + # - TauCetiBasic + # - Draconic + - type: MeleeWeapon + hidden: true + soundHit: + path: /Audio/Weapons/pierce.ogg + angle: 30 + animation: WeaponArcClaw + damage: + types: + Blunt: 5 + Slash: 12 + +- type: entity + id: WerewolfWhiteClaws # holy goida + parent: WerewolfWhite + components: + - type: MeleeWeapon + hidden: true + soundHit: + path: /Audio/Weapons/pierce.ogg + angle: 30 + animation: WeaponArcClaw + damage: + types: + Blunt: 5 + Slash: 12 + Holy: 8 # thats actually insane diff --git a/Resources/Prototypes/_Inky/Werewolf/Store/black.yml b/Resources/Prototypes/_Inky/Werewolf/Store/black.yml new file mode 100644 index 00000000000..4eb5cc4859d --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Store/black.yml @@ -0,0 +1,190 @@ +- type: listing + id: WerewolfBlack + name: werewolf-store-choose-black-name + description: werewolf-store-choose-black-desc + icon: + sprite: _Inky/Mobs/Werewolf/Mutations/black.rsi + state: icon + productEvent: !type:EventWerewolfChangeType + werewolfType: WerewolfTransformBlack + raiseProductEventOnUser: true + cost: + Fury: 15 + categories: + - WerewolfChoose + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + blacklist: + - WerewolfWhite + - WerewolfDirewolf + +- type: listing + id: WerewolfBiteBlack + name: werewolf-store-bite-black-name + description: werewolf-store-bite-black-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: black-bite + productEvent: !type:EventWerewolfUpgradeAbility + newActionId: ActionWerewolfBiteBlack + oldActionId: ActionWerewolfAbsorb + raiseProductEventOnUser: true + cost: + Fury: 10 + categories: + - WerewolfBlack + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfBlack + blacklist: + - WerewolfWhite + - WerewolfDirewolf + +- type: listing + id: WerewolfBlackLunar + name: werewolf-store-black-lunar-name + description: werewolf-store-lunar-black-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: lunar-black + productEvent: !type:WerewolfAddCollectivemind + popup: werewolf-black-lunar-popup + raiseProductEventOnUser: true + cost: + Fury: 6 + categories: + - WerewolfBlack + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfBlack + blacklist: + - WerewolfDirewolf + - WerewolfWhite + +- type: listing + id: WerewolfBlackOrder + name: werewolf-store-black-order-name + description: werewolf-store-lunar-order-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: howl-green + productEvent: !type:EventWerewolfUpgradeAbility + oldActionId: ActionWerewolfHowl + newActionId: ActionWerewolfHowlBlack + raiseProductEventOnUser: true + cost: + Fury: 6 + categories: + - WerewolfBlack + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfBlack + blacklist: + - WerewolfDirewolf + - WerewolfWhite + +- type: listing + id: WerewolfBlackBequeath + name: werewolf-store-black-bequeath-name + description: werewolf-store-bequeath-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: bequeath + productEvent: !type:EventWerewolfUpgradeAbility + newActionId: ActionWerewolfBequeath + raiseProductEventOnUser: true + cost: + Fury: 15 + categories: + - WerewolfBlack + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfBiteBlack + blacklist: + - WerewolfDirewolf + - WerewolfWhite + +- type: listing + id: WerewolfBlackBeckon + name: werewolf-store-black-beckon-name + description: werewolf-store-beckon-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: beckon + productEvent: !type:EventWerewolfUpgradeAbility + newActionId: ActionWerewolfBeckon + raiseProductEventOnUser: true + cost: + Fury: 10 + categories: + - WerewolfBlack + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfBlackLunar + blacklist: + - WerewolfDirewolf + - WerewolfWhite + +- type: listing + id: WerewolfBlackLunarApprentice + name: werewolf-store-black-lunar-name + description: werewolf-store-lunar-black-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: lunar-black + productEvent: !type:WerewolfAddCollectivemind + popup: werewolf-black-lunar-popup + raiseProductEventOnUser: true + cost: + Fury: 2 + categories: + - WerewolfBlackApprentice + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + blacklist: + - WerewolfDirewolf + - WerewolfWhite + - WerewolfBlack + +- type: listing + id: WerewolfBlackCall + name: werewolf-store-black-call-name + description: werewolf-store-black-call-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: call + productEvent: !type:EventWerewolfUpgradeAbility + newActionId: ActionWerewolfBlackCall + raiseProductEventOnUser: true + cost: + Fury: 20 + categories: + - WerewolfBlack + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfBlack + blacklist: + - WerewolfDirewolf + - WerewolfWhite diff --git a/Resources/Prototypes/_Inky/Werewolf/Store/dire.yml b/Resources/Prototypes/_Inky/Werewolf/Store/dire.yml new file mode 100644 index 00000000000..c81b85324f6 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Store/dire.yml @@ -0,0 +1,70 @@ +- type: listing + id: WerewolfDirewolf + name: werewolf-store-choose-direwolf-name + description: werewolf-store-choose-direwolf-desc + icon: + sprite: _Inky/Mobs/Werewolf/Mutations/direwolf.rsi + state: icon + productEvent: !type:EventWerewolfChangeType + werewolfType: WerewolfTransformDirewolf + raiseProductEventOnUser: true + cost: + Fury: 15 + categories: + - WerewolfChoose + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + blacklist: + - WerewolfWhite + - WerewolfBlack + +- type: listing + id: WerewolfHowlUpgrade + name: werewolf-store-howl-direwolf-name + description: werewolf-store-howl-direwolf-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: howl-red + productEvent: !type:EventWerewolfUpgradeAbility + oldActionId: ActionWerewolfHowl + newActionId: ActionWerewolfHowlDire + raiseProductEventOnUser: true + cost: + Fury: 5 + categories: + - WerewolfDire + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfDirewolf + blacklist: + - WerewolfWhite + - WerewolfBlack + +- type: listing + id: WerewolfBiteDire + name: werewolf-store-bite-direwolf-name + description: werewolf-store-bite-direwolf-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: bleeding-bite + productEvent: !type:EventWerewolfUpgradeAbility + newActionId: ActionWerewolfBiteDire + raiseProductEventOnUser: true + cost: + Fury: 10 + categories: + - WerewolfDire + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfDirewolf + blacklist: + - WerewolfWhite + - WerewolfBlack diff --git a/Resources/Prototypes/_Inky/Werewolf/Store/side.yml b/Resources/Prototypes/_Inky/Werewolf/Store/side.yml new file mode 100644 index 00000000000..c3d2d5c86c9 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Store/side.yml @@ -0,0 +1,53 @@ +- type: listing + id: WerewolfSideRegen + name: werewolf-store-regen-name + description: werewolf-store-regen-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: regen + productEvent: !type:EventWerewolfUpgradeAbility # holy goida + newActionId: ActionWerewolfRegen + raiseProductEventOnUser: true + cost: + Fury: 2 + categories: + - WerewolfSide + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: WerewolfSideAmbush + name: werewolf-store-jump-name + description: werewolf-store-jump-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: ambush + productEvent: !type:EventWerewolfUpgradeAbility + newActionId: ActionWerewolfJump + raiseProductEventOnUser: true + cost: + Fury: 2 + categories: + - WerewolfSide + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: WerewolfSideGut + name: werewolf-store-gut-name + description: werewolf-store-gut-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: gut + productEvent: !type:EventWerewolfUpgradeAbility + newActionId: ActionWerewolfGut + raiseProductEventOnUser: true + cost: + Fury: 2 + categories: + - WerewolfSide + conditions: + - !type:ListingLimitedStockCondition + stock: 1 diff --git a/Resources/Prototypes/_Inky/Werewolf/Store/store.yml b/Resources/Prototypes/_Inky/Werewolf/Store/store.yml new file mode 100644 index 00000000000..887e8a39200 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Store/store.yml @@ -0,0 +1,36 @@ +# all the store shit is here (or maybe not, todo idk) + +- type: currency + id: Fury + displayName: store-currency-display-fury + canWithdraw: false + +- type: storeCategory + id: WerewolfChoose + name: werewolf-store-choose + priority: 0 + +- type: storeCategory + id: WerewolfDire + name: werewolf-store-dire + priority: 1 + +- type: storeCategory + id: WerewolfWhite + name: werewolf-store-white + priority: 2 + +- type: storeCategory + id: WerewolfBlack + name: werewolf-store-black + priority: 3 + +- type: storeCategory + id: WerewolfBlackApprentice + name: werewolf-store-black-apprentice + priority: 98 + +- type: storeCategory + id: WerewolfSide + name: werewolf-store-side + priority: 99 diff --git a/Resources/Prototypes/_Inky/Werewolf/Store/white.yml b/Resources/Prototypes/_Inky/Werewolf/Store/white.yml new file mode 100644 index 00000000000..4941ab6f273 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/Store/white.yml @@ -0,0 +1,141 @@ +- type: listing + id: WerewolfWhite + name: werewolf-store-choose-white-name + description: werewolf-store-choose-white-desc + icon: + sprite: _Inky/Mobs/Werewolf/Mutations/white.rsi + state: icon + productEvent: !type:EventWerewolfChangeType + werewolfType: WerewolfTransformWhite + raiseProductEventOnUser: true + cost: + Fury: 15 + categories: + - WerewolfChoose + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + blacklist: + - WerewolfDirewolf + - WerewolfBlack + +- type: listing + id: WerewolfWhiteClaws + name: werewolf-store-white-dmg-name + description: werewolf-store-white-dmg-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: claws + productEvent: !type:EventWerewolfChangeType + werewolfType: WerewolfTransformWhiteClaws # holy goida + raiseProductEventOnUser: true + cost: + Fury: 10 + categories: + - WerewolfWhite + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfWhite + blacklist: + - WerewolfDirewolf + - WerewolfBlack + + +- type: listing + id: WerewolfWhiteBloodhound1 # todo werewolf + name: werewolf-store-white-track-name + description: werewolf-store-white-track-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: bloodhound + productEvent: !type:EventWerewolfUpgradeAbility + oldActionId: ActionWerewolfTransfurm + newActionId: ActionWerewolfTransfurmWhite + raiseProductEventOnUser: true + cost: + Fury: 6 + categories: + - WerewolfWhite + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfWhite + blacklist: + - WerewolfDirewolf + - WerewolfBlack + +#- type: listing +# id: WerewolfWhiteBloodhound +# name: werewolf-store-white-track-name +# description: werewolf-store-white-track-desc +# icon: +# sprite: _Inky/Actions/Werewolf/werewolf.rsi +# state: bloodhound +# productEvent: !type:EventWerewolfUpgradeAbility +# newActionId: ActionWerewolfBloodhound +# raiseProductEventOnUser: true +# cost: +# Fury: 6 +# categories: +# - WerewolfWhite +# conditions: +# - !type:ListingLimitedStockCondition +# stock: 1 +# - !type:BuyBeforeCondition +# whitelist: +# - WerewolfWhite +# blacklist: +# - WerewolfDirewolf + +- type: listing + id: WerewolfWhiteLunar + name: werewolf-store-white-lunar-name + description: werewolf-store-lunar-desc + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: lunar-white + productEvent: !type:WerewolfAddCollectivemind + popup: werewolf-white-lunar-popup + raiseProductEventOnUser: true + cost: + Fury: 15 + categories: + - WerewolfWhite + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfWhite + blacklist: + - WerewolfDirewolf + - WerewolfBlack + +- type: listing + id: WerewolfWhiteRevelation + name: werewolf-store-white-revelation-name + description: werewolf-store-revelation-desc # todo werewolf dont forget to say that it blocks you from buying new shit + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: revelation + productEvent: !type:WerewolfRevelationEvent + raiseProductEventOnUser: true + cost: + Fury: 20 + categories: + - WerewolfWhite + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyBeforeCondition + whitelist: + - WerewolfWhite + blacklist: + - WerewolfDirewolf + - WerewolfBlack diff --git a/Resources/Prototypes/_Inky/Werewolf/actions.yml b/Resources/Prototypes/_Inky/Werewolf/actions.yml new file mode 100644 index 00000000000..e29b9b88591 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/actions.yml @@ -0,0 +1,321 @@ +- type: entity + parent: BaseAction + id: ActionWerewolfHowl + name: Howl + description: Scream as loud as you can, stunning everyone in radius. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 30 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: howl + - type: InstantAction + event: !type:HowlEvent {} + - type: WerewolfAction + hungerCost: 30 + requireTransfurmed: true + +- type: entity + parent: BaseAction + id: ActionWerewolfTransfurm + name: Transfurm + description: Transfurm into a werewolf. Be warned as you can transfurm back only after 2 minutes. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: transfurm + - type: InstantAction + event: !type:TransfurmEvent {} + +- type: entity + parent: BaseAction + id: ActionWerewolfOpenMutationStore + name: Open mutation store + description: Open the mutation menu. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: store + - type: InstantAction + event: !type:EventWerewolfOpenStore {} + +- type: entity + id: PulseThermalVisionWerewolf + parent: ToggleThermalVision + name: Heightened Senses + description: Gives you 10 seconds of thermal vision + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 30 + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: senses + +- type: entity + parent: BaseAction + id: ActionWerewolfAbsorb + name: Bite + description: Bite and rip a limb from your fellow comrade for points. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 5 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: bite + - type: TargetAction + interactOnMiss: false + - type: EntityTargetAction + whitelist: + components: + - Body + canTargetSelf: false + event: !type:EventWerewolfDevour {} + +- type: entity + parent: BaseAction + id: ActionWerewolfGut + name: Gut + description: Spills one of the chest organs of a deceased crew member onto the floor. Organs that are produced from this ability grant you Fury. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: gut + - type: TargetAction + interactOnMiss: false + - type: EntityTargetAction + whitelist: + components: + - Body + canTargetSelf: false + event: !type:EventWerewolfGut {} + - type: WerewolfAction + hungerCost: 0 + requireTransfurmed: true + +- type: entity + parent: BaseAction + id: ActionWerewolfRegen + name: increased metabolism + description: Increase your metabolism, providing healing at the cost of a lot of hunger. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 10 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: regen + - type: InstantAction + event: !type:EventWerewolfRegen + - type: WerewolfAction + hungerCost: 65 +# requireTransfurmed: true + +- type: entity + id: ActionWerewolfJump + parent: BaseAction + name: Jump + description: Stun your enemies with a quick jump. Costs 30 hunger. + components: + - type: Action + checkCanInteract: false + priority: 0 + useDelay: 20 + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: ambush + - type: TargetAction + checkCanAccess: false + range: 10 + - type: WorldTargetAction + event: !type:WerewolfAmbushActionEvent + - type: WerewolfAction + hungerCost: 30 + requireTransfurmed: true + +# direwolf specific + +- type: entity + parent: BaseAction + id: ActionWerewolfHowlDire + name: Roar + description: Scream as loud as you can, stunning everyone in radius. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 30 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: howl-red + - type: InstantAction + event: !type:HowlEvent + shriekPower: 4 + stunDuration: 2 + - type: WerewolfAction + hungerCost: 45 # since its more powerful + requireTransfurmed: true + +- type: entity + parent: BaseAction + id: ActionWerewolfBiteDire + name: Bleeding bite + description: Sink your canines into a victim, and steal 30% of their blood. This will heal you slightly. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 30 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: bleeding-bite + - type: TargetAction + interactOnMiss: false + - type: EntityTargetAction + whitelist: + components: + - Body + canTargetSelf: false + event: !type:EventWerewolfBleedingBite { } + - type: WerewolfAction + hungerCost: 20 + requireTransfurmed: true + +# white specific + + +- type: entity + parent: BaseAction + id: ActionWerewolfTransfurmWhite + name: Transfurm + description: Transfurm into a werewolf. Be warned as you can transfurm back only after 2 minutes. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: transfurm + - type: InstantAction + event: !type:TransfurmWhiteEvent {} + +# black specific + +- type: entity + parent: BaseAction + id: ActionWerewolfBiteBlack + name: Bleeding bite + description: Bite a victim, causing massive blood loss. Has a 50% chance to infect the victim into a Werewolf under your rule. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 30 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: black-bite + - type: TargetAction + interactOnMiss: false + - type: EntityTargetAction + whitelist: + components: + - Body + canTargetSelf: false + event: !type:EventWerewolfBlackBite { } + - type: WerewolfAction + hungerCost: -30 + requireTransfurmed: true + +- type: entity + parent: BaseAction + id: ActionWerewolfHowlBlack + name: Alpha order + description: Howl and command every werewolf in radius to transform into their superior form, and heal those who already are transformed. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 30 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: howl-green + - type: InstantAction + event: !type:HowlEvent + shriekPower: 4 + stunDuration: 1 + forceTransfurm: true + healNearby: true + - type: WerewolfAction + hungerCost: 30 + requireTransfurmed: true + +- type: entity + parent: BaseAction + id: ActionWerewolfBeckon + name: Beckon + description: Silently transmits your location to your allied Werewolves + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 30 + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: beckon + - type: InstantAction + event: !type:WerewolfBeckonEvent + - type: WerewolfAction + hungerCost: 0 + +- type: entity + parent: BaseAction + id: ActionWerewolfBequeath + name: Bequeath + description: Crown an Unmutated Werewolf in your pack to become the next Black Wolf in case of your death. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: bequeath + - type: TargetAction + interactOnMiss: false + - type: EntityTargetAction + whitelist: + components: + - WerewolfAbilities + canTargetSelf: false + event: !type:EventWerewolfBequeath {} + - type: WerewolfAction + hungerCost: 0 + requireTransfurmed: true + +- type: entity + parent: BaseAction + id: ActionWerewolfBlackCall + name: Black Call + description: Force transform yourself, and everyone in your pack, permamently. Doubles you and your packs health. Requires a minimum of 4 pack members to use. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: NoItem + icon: + sprite: _Inky/Actions/Werewolf/werewolf.rsi + state: call + - type: InstantAction + event: !type:WerewolfBlackCallEvent {} diff --git a/Resources/Prototypes/_Inky/Werewolf/lycqc.yml b/Resources/Prototypes/_Inky/Werewolf/lycqc.yml new file mode 100644 index 00000000000..3b74bdc6d58 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/lycqc.yml @@ -0,0 +1,61 @@ +- type: entity + parent: BaseMartialArtsKnowledge + id: MartialArtLyCqc + name: LyCqc + components: + - type: CanPerformCombo + roundstartCombos: + - OpenVein + - ViciousToss + - Dismemberment + - type: SneakAttack + secondsTillHidden: 2 + +- type: combo + id: OpenVein + attacks: + - Grab + - Harm + - Harm + userEffects: + - !type:PlaySoundEffect + sound: /Audio/Weapons/genhit3.ogg + opponentEffects: + - !type:ModifyBleed + amount: 30 + - !type:HealthChange + damage: + types: + Slash: 30 + levelRequired: 10 + +- type: combo + id: ViciousToss + attacks: + - Disarm + - Grab + - Disarm + userEffects: + - !type:PlaySoundEffect + sound: /Audio/Weapons/genhit3.ogg + opponentEffects: + - !type:ThrowDirection + speed: 20 + - !type:ModifyKnockdown + time: 3 + levelRequired: 10 + +- type: combo + id: Dismemberment + attacks: + - Disarm + - Harm + - Disarm + - Harm + userEffects: + - !type:PlaySoundEffect + sound: /Audio/Weapons/genhit2.ogg + opponentEffects: + - !type:AmputateLimb + limbName: ArmLeft # GOIDA!! + levelRequired: 10 diff --git a/Resources/Prototypes/_Inky/Werewolf/polymorphs.yml b/Resources/Prototypes/_Inky/Werewolf/polymorphs.yml new file mode 100644 index 00000000000..ea69e9de787 --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/polymorphs.yml @@ -0,0 +1,113 @@ +- type: polymorph + id: WerewolfTransformBasic + configuration: + entity: WerewolfWerewolf + forced: true + inventory: None + transferName: false + transferDamage: true + revertOnCrit: true + revertOnDeath: false + polymorphSound: /Audio/_Goobstation/Changeling/Effects/armour_transform.ogg # goida + componentsToTransfer: + - component: WerewolfAbilities # todo werewolf KILL + - component: Store + - component: CollectiveMind + - component: WerewolfBit + - component: WerewolfInfectionImmune + - component: Hunger + +- type: polymorph + id: WerewolfTransformWerehuman + configuration: + entity: WerewolfWerehuman + forced: true + inventory: None + revertOnCrit: true + revertOnDeath: false + transferName: false + transferDamage: true + polymorphSound: /Audio/_Goobstation/Changeling/Effects/armour_transform.ogg # goida + componentsToTransfer: + - component: WerewolfAbilities + - component: Store + - component: CollectiveMind + - component: WerewolfBit + - component: WerewolfInfectionImmune + - component: Hunger + +- type: polymorph + id: WerewolfTransformDirewolf + configuration: + entity: WerewolfDirewolf + forced: true + revertOnCrit: true + revertOnDeath: false + inventory: None + transferName: false + transferDamage: true + polymorphSound: /Audio/_Goobstation/Changeling/Effects/armour_transform.ogg + componentsToTransfer: + - component: WerewolfAbilities + - component: Store + - component: CollectiveMind + - component: WerewolfBit + - component: WerewolfInfectionImmune + - component: Hunger + +- type: polymorph + id: WerewolfTransformWhite + configuration: + entity: WerewolfWhite + forced: true + revertOnCrit: true + revertOnDeath: false + inventory: None + transferName: false + transferDamage: true + polymorphSound: /Audio/_Goobstation/Changeling/Effects/armour_transform.ogg + componentsToTransfer: + - component: WerewolfAbilities + - component: Store + - component: CollectiveMind + - component: WerewolfBit + - component: WerewolfInfectionImmune + - component: Hunger + +- type: polymorph + id: WerewolfTransformWhiteClaws # goida + configuration: + entity: WerewolfWhiteClaws + forced: true + revertOnCrit: true + revertOnDeath: false + inventory: None + transferName: false + transferDamage: true + polymorphSound: /Audio/_Goobstation/Changeling/Effects/armour_transform.ogg + componentsToTransfer: + - component: WerewolfAbilities + - component: Store + - component: CollectiveMind + - component: WerewolfBit + - component: WerewolfInfectionImmune + - component: Hunger + +- type: polymorph + id: WerewolfTransformBlack + configuration: + entity: WerewolfBlackwolf + forced: true + revertOnCrit: true + revertOnDeath: false + inventory: None + transferName: false + transferDamage: true + polymorphSound: /Audio/_Goobstation/Changeling/Effects/armour_transform.ogg + componentsToTransfer: + - component: WerewolfAbilities + - component: Store + - component: CollectiveMind + - component: WerewolfBit + - component: WerewolfInfectionImmune + - component: Hunger diff --git a/Resources/Prototypes/_Inky/Werewolf/werewolf.yml b/Resources/Prototypes/_Inky/Werewolf/werewolf.yml new file mode 100644 index 00000000000..9ab9f3878fa --- /dev/null +++ b/Resources/Prototypes/_Inky/Werewolf/werewolf.yml @@ -0,0 +1,30 @@ +- type: antag + id: Werewolf + name: roles-antag-werewolf-name + antagonist: true + setPreference: true + objective: roles-antag-werewolf-desc + requirements: + - !type:SpeciesRequirement + inverted: true + species: + - IPC + - Plasmaman + guides: [ ] + creditImage: /Textures/_Trauma/EndCredits/Antags/zombies.png # todo werewolf + +- type: antagSpecifier + id: Werewolf + blacklist: + components: + - AntagImmune + - Changeling + prefRoles: + - Werewolf + briefing: + text: werewolf-role-greeting + color: Brown + sound: "/Audio/_Inky/Antag/Werewolf/werewolf_start.ogg" +# components: + mindRoles: + - MindRoleWerewolf diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/ambush.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/ambush.png new file mode 100644 index 00000000000..7f1160c6c08 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/ambush.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/beckon.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/beckon.png new file mode 100644 index 00000000000..f4d3f0c8238 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/beckon.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bequeath.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bequeath.png new file mode 100644 index 00000000000..79a779cf315 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bequeath.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bite.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bite.png new file mode 100644 index 00000000000..cb45f1a76ac Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bite.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/black-bite.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/black-bite.png new file mode 100644 index 00000000000..3aee530faac Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/black-bite.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bleeding-bite.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bleeding-bite.png new file mode 100644 index 00000000000..153902d7940 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bleeding-bite.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bloodhound.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bloodhound.png new file mode 100644 index 00000000000..f27ccc9e768 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/bloodhound.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/call.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/call.png new file mode 100644 index 00000000000..b10f4613e98 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/call.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/claws.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/claws.png new file mode 100644 index 00000000000..4ca95116987 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/claws.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/empty.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/empty.png new file mode 100644 index 00000000000..cfd488422e8 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/empty.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/gut.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/gut.png new file mode 100644 index 00000000000..31f3d090e9d Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/gut.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl-green.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl-green.png new file mode 100644 index 00000000000..271a9a78252 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl-green.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl-red.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl-red.png new file mode 100644 index 00000000000..c70095f9550 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl-red.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl.png new file mode 100644 index 00000000000..1dad340bc52 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/howl.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/lunar-black.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/lunar-black.png new file mode 100644 index 00000000000..a5c5affd008 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/lunar-black.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/lunar-white.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/lunar-white.png new file mode 100644 index 00000000000..a7ac0d8204c Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/lunar-white.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/meta.json b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/meta.json new file mode 100644 index 00000000000..a06786ca634 --- /dev/null +++ b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/meta.json @@ -0,0 +1,74 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by TechnoSpaghetti(192358052710711299) on discord for Goob Station", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "empty" + }, + { + "name": "howl" + }, + { + "name": "howl-red" + }, + { + "name": "howl-green" + }, + { + "name": "transfurm" + }, + { + "name": "senses" + }, + { + "name": "bite" + }, + { + "name": "store" + }, + { + "name": "regen" + }, + { + "name": "ambush" + }, + { + "name": "gut" + }, + { + "name": "bleeding-bite" + }, + { + "name": "claws" + }, + { + "name": "bloodhound" + }, + { + "name": "lunar-white" + }, + { + "name": "lunar-black" + }, + { + "name": "revelation" + }, + { + "name": "black-bite" + }, + { + "name": "beckon" + }, + { + "name": "bequeath" + }, + { + "name": "call" + } + ] +} diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/regen.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/regen.png new file mode 100644 index 00000000000..d8d3e151a7f Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/regen.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/revelation.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/revelation.png new file mode 100644 index 00000000000..680ee42b758 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/revelation.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/senses.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/senses.png new file mode 100644 index 00000000000..dfb2a800826 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/senses.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/store.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/store.png new file mode 100644 index 00000000000..56db4b71b0a Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/store.png differ diff --git a/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/transfurm.png b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/transfurm.png new file mode 100644 index 00000000000..d98fdca10e8 Binary files /dev/null and b/Resources/Textures/_Inky/Actions/Werewolf/werewolf.rsi/transfurm.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/icon.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/icon.png new file mode 100644 index 00000000000..593168a7915 Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/icon.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/meta.json b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/meta.json new file mode 100644 index 00000000000..146ecd17aca --- /dev/null +++ b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "size": + { + "x": 64, + "y": 64 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by TechnoSpaghetti(192358052710711299) on discord for Goobstation", + "states": + [ + { + "name": "icon" + }, + { + "name": "werewolf", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/werewolf.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/werewolf.png new file mode 100644 index 00000000000..99883d19257 Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/base.rsi/werewolf.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/icon.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/icon.png new file mode 100644 index 00000000000..c9d9849cb43 Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/icon.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/meta.json b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/meta.json new file mode 100644 index 00000000000..617bd33dc39 --- /dev/null +++ b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "size": + { + "x": 64, + "y": 64 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by TechnoSpaghetti(192358052710711299) on discord for Goobstation", + "states": + [ + { + "name": "icon" + }, + { + "name": "wolf", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/wolf.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/wolf.png new file mode 100644 index 00000000000..72fcd01cb2d Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/black.rsi/wolf.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/direwolf.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/direwolf.png new file mode 100644 index 00000000000..03b501df94e Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/direwolf.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/icon.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/icon.png new file mode 100644 index 00000000000..7effab8ad6f Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/icon.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/meta.json b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/meta.json new file mode 100644 index 00000000000..28f9a683eda --- /dev/null +++ b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/direwolf.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "size": + { + "x": 64, + "y": 64 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by TechnoSpaghetti(192358052710711299) on discord for Goobstation", + "states": + [ + { + "name": "icon" + }, + { + "name": "direwolf", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/icon.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/icon.png new file mode 100644 index 00000000000..d4551937286 Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/icon.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/meta.json b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/meta.json new file mode 100644 index 00000000000..cc8b87ffa79 --- /dev/null +++ b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "size": + { + "x": 64, + "y": 64 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by TechnoSpaghetti(192358052710711299) on discord for Goobstation", + "states": + [ + { + "name": "icon" + }, + { + "name": "werehuman", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/werehuman.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/werehuman.png new file mode 100644 index 00000000000..1f22fc1c067 Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/werehuman.rsi/werehuman.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/icon.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/icon.png new file mode 100644 index 00000000000..6ad4ff9f117 Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/icon.png differ diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/meta.json b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/meta.json new file mode 100644 index 00000000000..617bd33dc39 --- /dev/null +++ b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "size": + { + "x": 64, + "y": 64 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by TechnoSpaghetti(192358052710711299) on discord for Goobstation", + "states": + [ + { + "name": "icon" + }, + { + "name": "wolf", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/wolf.png b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/wolf.png new file mode 100644 index 00000000000..6d07ba99602 Binary files /dev/null and b/Resources/Textures/_Inky/Mobs/Werewolf/Mutations/white.rsi/wolf.png differ