diff --git a/Content.Client/_Sunrise/FleshCult/FleshCultistSystem.cs b/Content.Client/_Sunrise/FleshCult/FleshCultistSystem.cs index c2407090e7..b8c79611ca 100644 --- a/Content.Client/_Sunrise/FleshCult/FleshCultistSystem.cs +++ b/Content.Client/_Sunrise/FleshCult/FleshCultistSystem.cs @@ -3,7 +3,7 @@ using Content.Shared.StatusIcon.Components; using Robust.Shared.Prototypes; namespace Content.Client._Sunrise.FleshCult; -public sealed class FleshCultistSystem : SharedFleshMobSystem +public sealed class FleshCultistSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototype = default!; diff --git a/Content.Client/_Sunrise/FleshCult/FleshHuggerSystem.cs b/Content.Client/_Sunrise/FleshCult/FleshHuggerSystem.cs deleted file mode 100644 index a79fe76221..0000000000 --- a/Content.Client/_Sunrise/FleshCult/FleshHuggerSystem.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Flesh; - -namespace Content.Client._Sunrise.FleshCult; - -public sealed class FleshHuggerSystem: SharedFleshHuggerSystem -{ - - public override void Initialize() - { - } -} diff --git a/Content.Client/_Sunrise/FleshCult/FleshMobSystem.cs b/Content.Client/_Sunrise/FleshCult/FleshMobSystem.cs deleted file mode 100644 index 64466b6eed..0000000000 --- a/Content.Client/_Sunrise/FleshCult/FleshMobSystem.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Shared._Sunrise.FleshCult; - -namespace Content.Client._Sunrise.FleshCult; - -public sealed class FleshMobSystem : SharedFleshCultistSystem -{ - public override void Initialize() - { - base.Initialize(); - } -} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs index b0551fe597..6ab1874e0c 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs @@ -35,6 +35,18 @@ public sealed partial class AdminVerbSystem [ValidatePrototypeId] private const string DefaultThiefRule = "Thief"; + [ValidatePrototypeId] + private const string DefaultVampireRule = "Vampire"; + + [ValidatePrototypeId] + private const string DefaultChangelingRule = "Changeling"; + + [ValidatePrototypeId] + private const string DefaultFleshCultRule = "FleshCult"; + + [ValidatePrototypeId] + private const string DefaultAssaultOpsRule = "AssaultOps"; + [ValidatePrototypeId] private const string PirateGearId = "PirateGear"; @@ -161,7 +173,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Changeling/changeling_abilities.rsi"), "transform"), Act = () => { - _antag.ForceMakeAntag(targetPlayer, "Changeling"); + _antag.ForceMakeAntag(targetPlayer, DefaultChangelingRule); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-changeling"), @@ -175,7 +187,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Actions/actions_vampire.rsi"), "unholystrength"), Act = () => { - _antag.ForceMakeAntag(targetPlayer, "Vampire"); + _antag.ForceMakeAntag(targetPlayer, DefaultVampireRule); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-vampire"), @@ -189,13 +201,12 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster46_contraband"), Act = () => { - _antag.ForceMakeAntag(targetPlayer, "AssaultOps"); + _antag.ForceMakeAntag(targetPlayer, DefaultAssaultOpsRule); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-assault-operative"), }; - // На время пока не будут закончены все новые режимы. - //args.Verbs.Add(assaultOperative); + args.Verbs.Add(assaultOperative); Verb fleshCultist = new() { @@ -204,12 +215,11 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Texture(new ResPath("_Sunrise/FleshCult/Interface/Actions/fleshCultistFleshHeart.png")), Act = () => { - _antag.ForceMakeAntag(targetPlayer, "FleshCult"); + _antag.ForceMakeAntag(targetPlayer, DefaultFleshCultRule); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-flesh-cultist"), }; - // На время пока не будут закончены все новые режимы. - //args.Verbs.Add(fleshCultist); + args.Verbs.Add(fleshCultist); } } diff --git a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs index 9b78482cc5..83736bd92b 100644 --- a/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs +++ b/Content.Server/GameTicking/Commands/SetGamePresetCommand.cs @@ -7,7 +7,7 @@ using Robust.Shared.Prototypes; namespace Content.Server.GameTicking.Commands { - [AdminCommand(AdminFlags.Host)] // На время пока не будут закончены все новые режимы. + [AdminCommand(AdminFlags.Round)] public sealed class SetGamePresetCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entity = default!; diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index 02cfaf76f7..cf0b0eceb1 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -327,7 +327,7 @@ public sealed partial class GameTicker #region Command Implementations - [AdminCommand(AdminFlags.Host)] // На время пока не будут закончены все новые режимы. + [AdminCommand(AdminFlags.Fun)] private void AddGameRuleCommand(IConsoleShell shell, string argstr, string[] args) { if (args.Length == 0) diff --git a/Content.Server/_Sunrise/FleshCult/FleshAbilitiesComponent.cs b/Content.Server/_Sunrise/FleshCult/FleshAbilitiesComponent.cs new file mode 100644 index 0000000000..ee1f715c8c --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshAbilitiesComponent.cs @@ -0,0 +1,81 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Server._Sunrise.FleshCult +{ + [RegisterComponent] + public sealed partial class FleshAbilitiesComponent : Component + { + [DataField("startingActions", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List StartingActions = new(); + + [DataField] + public List Actions = new(); + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ActionFleshCultistDevourId = "FleshCultistDevour"; + + [ViewVariables(VVAccess.ReadWrite), DataField("bloodWhitelist")] + public List BloodWhitelist = new() + { + "Blood", + "CopperBlood", + "InsectBlood", + "AmmoniaBlood", + "ZombieBlood" + }; + + [DataField("devourTime")] public float DevourTime = 10f; + + [DataField("devourSound")] + public SoundSpecifier DevourSound = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/devour_flesh_cultist.ogg"); + + [DataField("adrenalinReagents")] public Solution AdrenalinReagents = new() + { + Contents = { new ReagentQuantity(new ReagentId("Ephedrine", null), 10) } + }; + + [DataField("healDevourReagents")] public Solution HealDevourReagents = new() + { + Contents = + { + new ReagentQuantity(new ReagentId("Carol", null), 20), + } + }; + + [DataField("healBloodAbsorbReagents")] public Solution HealBloodAbsorbReagents = new() + { + Contents = + { + new ReagentQuantity(new ReagentId("Carol", null), 1), + } + }; + + [DataField] + public SoundSpecifier BloodAbsorbSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg"); + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string BulletAcidSpawnId = "BulletSplashAcid"; + + [DataField] + public SoundSpecifier SoundBulletAcid = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/flesh_cultist_mutation.ogg"); + + [DataField] + public SoundSpecifier SoundMutation = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/flesh_cultist_mutation.ogg"); + + [DataField("fleshHeartId", customTypeSerializer: typeof(PrototypeIdSerializer)), + ViewVariables(VVAccess.ReadWrite)] + public string FleshHeartId = "FleshHeart"; + + [ViewVariables(VVAccess.ReadWrite), DataField("soundThrowWorm")] + public SoundSpecifier? SoundThrowHugger = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/throw_worm.ogg"); + + [ViewVariables(VVAccess.ReadWrite), + DataField("huggerMobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string HuggerMobSpawnId = "MobFleshHugger"; + } +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshBodyModComponent.cs b/Content.Server/_Sunrise/FleshCult/FleshBodyModComponent.cs new file mode 100644 index 0000000000..ab23f385e0 --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshBodyModComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server._Sunrise.FleshCult +{ + [RegisterComponent] + public sealed partial class FleshBodyModComponent : Component + { + + } +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs new file mode 100644 index 0000000000..0184af507d --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs @@ -0,0 +1,569 @@ +using System.Linq; +using System.Numerics; +using Content.Server.Body.Components; +using Content.Server.Construction.Components; +using Content.Server.Traits.Assorted; +using Content.Shared._Sunrise.FleshCult; +using Content.Shared.Body.Components; +using Content.Shared.Body.Part; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Coordinates.Helpers; +using Content.Shared.Cuffs.Components; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Content.Shared.Fluids.Components; +using Content.Shared.Hands.Components; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Maps; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Robust.Shared.Audio; +using Robust.Shared.Collections; +using Robust.Shared.Containers; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Player; + +namespace Content.Server._Sunrise.FleshCult; + +public sealed partial class FleshCultSystem +{ + private void InitializeAbilities() + { + SubscribeLocalEvent(OnHandTransformEvent); + SubscribeLocalEvent(OnBodyModificationAction); + SubscribeLocalEvent(OnAdrenalinActionEvent); + SubscribeLocalEvent(OnCreateFleshHeartActionEvent); + SubscribeLocalEvent(OnThrowHugger); + SubscribeLocalEvent(OnAcidSpit); + SubscribeLocalEvent(AbsormBloodPool); + SubscribeLocalEvent(OnDevourAction); + SubscribeLocalEvent(OnDevourDoAfter); + SubscribeLocalEvent(OnUnlockAbility); + + SubscribeLocalEvent(OnStartup); + } + + private void OnUnlockAbility(EntityUid uid, FleshAbilitiesComponent component, + FleshCultistUnlockAbilityEvent args) + { + var action = _action.AddAction(uid, args.Prototype); + if (action != null) + component.Actions.Add(action.Value); + } + + private void OnStartup(EntityUid uid, FleshAbilitiesComponent component, ComponentStartup args) + { + foreach (var componentStartingAction in component.StartingActions) + { + var action = _action.AddAction(uid, componentStartingAction); + if (action != null) + component.Actions.Add(action.Value); + } + } + + private void OnDevourAction(EntityUid uid, FleshAbilitiesComponent component, FleshCultistDevourActionEvent args) + { + if (args.Handled) + return; + + var target = args.Target; + + if (!TryComp(target, out var targetState)) + return; + if (!TryComp(target, out var bloodstream)) + return; + var hasAppearance = false; + { + switch (targetState.CurrentState) + { + case MobState.Dead: + if (EntityManager.TryGetComponent(target, out HumanoidAppearanceComponent? humanoidAppearance)) + { + if (!_speciesWhitelist.Contains(humanoidAppearance.Species)) + { + _popup.PopupEntity( + Loc.GetString("flesh-cultist-devout-target-not-have-flesh"), + uid, uid); + return; + } + + if (TryComp(target, out var fixturesComponent)) + { + if (fixturesComponent.Fixtures["fix1"].Density <= 60) + { + _popup.PopupEntity( + Loc.GetString("flesh-cultist-devout-target-invalid"), + uid, uid); + return; + } + } + + hasAppearance = true; + } + else + { + if (!component.BloodWhitelist.Contains(bloodstream.BloodReagent)) + { + _popup.PopupEntity( + Loc.GetString("flesh-cultist-devout-target-not-have-flesh"), + uid, uid); + return; + } + if (bloodstream.BloodMaxVolume < 30) + { + _popup.PopupEntity( + Loc.GetString("flesh-cultist-devout-target-invalid"), + uid, uid); + return; + } + } + var saturation = MatchSaturation(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); + if (TryComp(uid, out var fleshCultistComponent) && + fleshCultistComponent.Hunger + saturation >= fleshCultistComponent.MaxHunger) + { + _popup.PopupEntity( + Loc.GetString("flesh-cultist-devout-not-hungry"), + uid, uid); + return; + } + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager ,uid, component.DevourTime, + new FleshCultistDevourDoAfterEvent(), uid, target: target, used: uid) + { + BreakOnMove = true, + }); + args.Handled = true; + break; + + case MobState.Invalid: + case MobState.Critical: + case MobState.Alive: + default: + _popup.PopupEntity( + Loc.GetString("flesh-cultist-devout-target-alive"), + uid, uid); + break; + } + } + } + + private void OnDevourDoAfter(EntityUid uid, FleshAbilitiesComponent component, FleshCultistDevourDoAfterEvent args) + { + if (args.Handled || args.Cancelled) + return; + + if (args.Args.Target == null) + return; + + if (!TryComp(args.Args.Target.Value, out var bloodstream)) + return; + + var hasAppearance = false; + + var xform = Transform(args.Args.Target.Value); + var coordinates = xform.Coordinates; + _audioSystem.PlayPvs(component.DevourSound, coordinates, AudioParams.Default.WithVariation(0.025f).WithMaxDistance(5f)); + _popup.PopupEntity(Loc.GetString("flesh-cultist-devour-target", + ("Entity", uid), ("Target", args.Args.Target)), uid); + + if (bloodstream.BloodSolution != null) + { + _bloodstreamSystem.SpillAllSolutions(args.Args.Target.Value, bloodstream); + } + + if (TryComp(args.Args.Target, out var HuAppComponent)) + { + if (TryComp(args.Args.Target.Value, out ContainerManagerComponent? container)) + { + foreach (var cont in container.GetAllContainers().ToArray()) + { + foreach (var ent in cont.ContainedEntities.ToArray()) + { + if (HasComp(ent)) + { + continue; + } + _containerSystem.Remove(ent, cont, force: true); + Transform(ent).Coordinates = coordinates; + } + } + } + + // SUNRISE-TODO: Убрать конечности хирургией а тело заменить на скелета + if (TryComp(args.Args.Target, out var bodyComponent)) + { + var parts = _body.GetBodyChildren(args.Args.Target, bodyComponent).ToArray(); + + foreach (var part in parts) + { + if (part.Component.PartType == BodyPartType.Head) + continue; + + if (part.Component.PartType == BodyPartType.Torso) + { + foreach (var organ in _body.GetPartOrgans(part.Id, part.Component)) + { + //_body.RemoveOrgan(organ.Id); + QueueDel(organ.Id); + } + } + else + { + QueueDel(part.Id); + } + } + } + + var skeletonSprites = _prototypeManager.Index("MobSkeletonSprites"); + foreach (var (key, id) in skeletonSprites.Sprites) + { + if (key != HumanoidVisualLayers.Head) + { + _sharedHuApp.SetBaseLayerId(args.Args.Target.Value, key, id, humanoid: HuAppComponent); + } + } + + if (TryComp(args.Args.Target, out var fixturesComponent)) + { + _physics.SetDensity(args.Args.Target.Value, "fix1", fixturesComponent.Fixtures["fix1"], 50); + } + + if (TryComp(args.Args.Target, out var appComponent)) + { + _sharedAppearance.SetData(args.Args.Target.Value, DamageVisualizerKeys.Disabled, true, appComponent); + } + + hasAppearance = true; + } + + var saturation = MatchSaturation(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); + var evolutionPoint = MatchEvolutionPoint(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); + var healPoint = MatchHealPoint(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); + + RemComp(args.Args.Target.Value); + + EnsureComp(args.Args.Target.Value); + + if (!hasAppearance) + { + QueueDel(args.Args.Target.Value); + } + + if (_solutionContainerSystem.TryGetInjectableSolution(uid, out var injectableSolution, out _)) + { + var transferSolution = new Solution(); + foreach (var solution in component.HealDevourReagents) + { + transferSolution.AddReagent(solution.Reagent, solution.Quantity * healPoint); + } + _solutionContainerSystem.TryAddSolution(injectableSolution.Value, transferSolution); + } + + if (TryComp(uid, out var fleshCultistComponent)) + { + fleshCultistComponent.Hunger += saturation; + _store.TryAddCurrency(new Dictionary + { {fleshCultistComponent.StolenCurrencyPrototype, evolutionPoint} }, uid); + } + + } + + private void AbsormBloodPool(EntityUid uid, + FleshAbilitiesComponent component, + FleshCultistAbsorbBloodPoolActionEvent args) + { + if (args.Handled) + return; + + var xform = Transform(uid); + var puddles = new ValueList<(EntityUid Entity, string Solution)>(); + puddles.Clear(); + foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, 1f)) + { + if (TryComp(entity, out var puddle)) + { + puddles.Add((entity, puddle.SolutionName)); + } + } + + if (puddles.Count == 0) + { + _popup.PopupEntity(Loc.GetString("flesh-cultist-not-find-puddles"), + uid, uid, PopupType.Large); + return; + } + + var absorbBlood = new Solution(); + foreach (var (puddle, solution) in puddles) + { + if (!_solutionContainerSystem.TryGetSolution(puddle, solution, out var puddleSolution)) + { + continue; + } + foreach (var puddleSolutionContent in puddleSolution.Value.Comp.Solution.ToList()) + { + if (!component.BloodWhitelist.Contains(puddleSolutionContent.Reagent.Prototype)) + continue; + + var blood = puddleSolution.Value.Comp.Solution.SplitSolutionWithOnly( + puddleSolutionContent.Quantity, puddleSolutionContent.Reagent.Prototype); + + absorbBlood.AddSolution(blood, _prototypeManager); + } + + var ev = new SolutionContainerChangedEvent(puddleSolution.Value.Comp.Solution, solution); + RaiseLocalEvent(puddle, ref ev); + } + + if (absorbBlood.Volume == 0) + { + _popup.PopupEntity(Loc.GetString("flesh-cultist-cant-absorb-puddle"), + uid, uid, PopupType.Large); + return; + } + + _audioSystem.PlayPvs(component.BloodAbsorbSound, uid); + _popup.PopupEntity(Loc.GetString("flesh-cultist-absorb-puddle", ("Entity", uid)), + uid, uid, PopupType.Large); + + var transferSolution = new Solution(); + foreach (var solution in component.HealBloodAbsorbReagents) + { + transferSolution.AddReagent(solution.Reagent, solution.Quantity * (absorbBlood.Volume / 10)); + } + + if (_solutionContainerSystem.TryGetInjectableSolution(uid, out var injectableSolution, out var _)) + { + _solutionContainerSystem.TryAddSolution(injectableSolution.Value, transferSolution); + } + absorbBlood.RemoveAllSolution(); + + args.Handled = true; + } + + private void OnAcidSpit(EntityUid uid, FleshAbilitiesComponent component, FleshCultistAcidSpitActionEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + var acidBullet = Spawn(component.BulletAcidSpawnId, Transform(uid).Coordinates); + var xform = Transform(uid); + var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); + var direction = mapCoords.Position - xform.MapPosition.Position; + var userVelocity = _physics.GetMapLinearVelocity(uid); + + _gunSystem.ShootProjectile(acidBullet, direction, userVelocity, uid, uid); + _audioSystem.PlayPvs(component.SoundBulletAcid, uid, component.SoundBulletAcid.Params); + } + + private void OnHandTransformEvent(EntityUid uid, FleshAbilitiesComponent component, FleshCultistHandTransformEvent args) + { + if (args.Handled) + return; + + if (TryComp(uid, out var cuffableComponent) && cuffableComponent.CuffedHandCount > 0) + { + _cuffable.Uncuff(uid, uid, cuffableComponent.LastAddedCuffs); + } + + var hands = _handsSystem.EnumerateHands(uid); + var enumerateHands = hands as Hand[] ?? Enumerable.ToArray(hands); + foreach (var hand in enumerateHands) + { + if (hand.Container == null) + continue; + + foreach (var containedEntity in hand.Container.ContainedEntities) + { + if (!TryComp(containedEntity, out MetaDataComponent? metaData) || metaData.EntityPrototype == null) + continue; + + if (!HasComp(containedEntity)) + { + if (hand != enumerateHands.First()) + continue; + + var isDrop = _handsSystem.TryDrop(uid, checkActionBlocker: false); + if (metaData.EntityPrototype.ID == args.Prototype) + continue; + + if (isDrop) + continue; + + _popup.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), uid, uid, PopupType.Large); + return; + } + + if (metaData.EntityPrototype.ID == args.Prototype) + { + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + _popup.PopupEntity(Loc.GetString("flesh-cultist-transform-mod-to-hand", + ("User", uid), ("Mod", containedEntity)), uid, PopupType.LargeCaution); + QueueDel(containedEntity); + EnsureComp(uid); + args.Handled = true; + return; + } + } + } + + var modEntity = Spawn(args.Prototype, Transform(uid).Coordinates); + var isPickup = _handsSystem.TryPickup(uid, modEntity, checkActionBlocker: false, animateUser: false, animate: false); + if (isPickup) + { + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + _popup.PopupEntity(Loc.GetString("flesh-cultist-transform-hand-to-mod", ("User", uid), ("Mod", modEntity)), uid, PopupType.LargeCaution); + + // Удаляем компонент наручников, если есть + if (HasComp(uid)) + { + EntityManager.RemoveComponent(uid); + } + } + else + { + Logger.Error($"Failed to equip {args.Prototype} to hand, removing entity"); + QueueDel(modEntity); + } + + args.Handled = true; + } + + private void OnBodyModificationAction( + EntityUid uid, + FleshAbilitiesComponent component, + FleshCultistBodyTransformEvent args) + { + if (args.Handled) + return; + + foreach (var slot in args.CheckSlots) + { + if (_inventory.TryGetSlotEntity(uid, slot, out var entity) && + HasComp(entity)) + { + _popup.PopupEntity(Loc.GetString("flesh-cultist-transform-conflict"), + uid, uid, PopupType.Large); + return; + } + } + + _inventory.TryGetSlotEntity(uid, args.TargetSlot, out var equippedItem); + if (equippedItem != null) + { + if (HasComp(equippedItem.Value)) + { + _popup.PopupEntity(Loc.GetString("flesh-cultist-transform-conflict"), + uid, uid, PopupType.Large); + return; + } + + if (TryComp(equippedItem.Value, out MetaDataComponent? metaData) && metaData.EntityPrototype != null && + metaData.EntityPrototype.ID == args.Prototype) + { + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + _popup.PopupEntity(Loc.GetString("flesh-cultist-transform-body-remove", + ("User", uid), ("Mod", equippedItem)), uid, PopupType.LargeCaution); + EntityManager.DeleteEntity(equippedItem.Value); + _movement.RefreshMovementSpeedModifiers(uid); + args.Handled = true; + return; + } + + _inventory.TryUnequip(uid, args.TargetSlot, true, true); + } + + var newBodyMod = Spawn(args.Prototype, Transform(uid).Coordinates); + var equipped = _inventory.TryEquip(uid, newBodyMod, args.TargetSlot, true); + if (!equipped) + { + QueueDel(newBodyMod); + } + else + { + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + _popup.PopupEntity(Loc.GetString("flesh-cultist-transform-body-add", + ("User", uid), ("Mod", newBodyMod)), uid, PopupType.LargeCaution); + args.Handled = true; + } + } + + private void OnAdrenalinActionEvent(EntityUid uid, FleshAbilitiesComponent component, FleshCultistAdrenalinActionEvent args) + { + if (!_solutionContainerSystem.TryGetInjectableSolution(uid, out var injectableSolution, out var _)) + return; + var transferSolution = new Solution(); + foreach (var solution in component.AdrenalinReagents) + { + transferSolution.AddReagent(solution.Reagent, solution.Quantity); + } + _solutionContainerSystem.TryAddSolution(injectableSolution.Value, transferSolution); + args.Handled = true; + } + + private void OnCreateFleshHeartActionEvent(EntityUid uid, FleshAbilitiesComponent component, FleshCultistCreateFleshHeartActionEvent args) + { + var xform = Transform(uid); + var radius = 1.5f; + if (!TryComp(xform.GridUid, out var grid)) + { + _popup.PopupEntity(Loc.GetString("flesh-cultist-cant-spawn-flesh-heart-in-space", + ("Entity", uid)), uid, PopupType.Large); + return; + } + + var offsetValue = Vector2Helpers.Normalized(xform.LocalRotation.ToWorldVec()); + var targetCord = xform.Coordinates.Offset(offsetValue).SnapToGrid(EntityManager); + var tilerefs = Enumerable.ToArray(grid.GetLocalTilesIntersecting( + new Box2(targetCord.Position + new Vector2(-radius, -radius), targetCord.Position + new Vector2(radius, radius)))); + foreach (var tileref in tilerefs) + { + foreach (var entity in tileref.GetEntitiesInTile()) + { + PhysicsComponent? physics = null; // We use this to check if it's impassable + if (HasComp(entity) && entity != uid || // Is it a mob? + Resolve(entity, ref physics, false) && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0 || + HasComp(entity) && entity != uid) // Is construction? + { + _popup.PopupEntity(Loc.GetString("flesh-cultist-cant-spawn-flesh-heart-here", + ("Entity", uid)), uid, PopupType.Large); + return; + } + } + } + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + EntityManager.SpawnEntity(component.FleshHeartId, targetCord); + args.Handled = true; + } + + private void OnThrowHugger(EntityUid uid, FleshAbilitiesComponent component, FleshCultistThrowHuggerActionEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + var hugger = Spawn(component.HuggerMobSpawnId, Transform(uid).Coordinates); + var xform = Transform(uid); + var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); + var direction = mapCoords.Position - xform.MapPosition.Position; + + _throwing.TryThrow(hugger, direction, 7F, uid, 10F); + if (component.SoundThrowHugger != null) + { + _audioSystem.PlayPvs(component.SoundThrowHugger, uid, component.SoundThrowHugger.Params); + } + _popup.PopupEntity(Loc.GetString("flesh-cultist-throw-hugger"), uid, uid, + PopupType.LargeCaution); + _popup.PopupEntity(Loc.GetString("flesh-cultist-throw-hugger-others", ("Entity", uid)), + uid, Filter.PvsExcept(uid), true, PopupType.LargeCaution); + } + +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs new file mode 100644 index 0000000000..eeaf9a7b27 --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs @@ -0,0 +1,432 @@ +using System.Linq; +using Content.Server.Atmos.Components; +using Content.Server.Body.Components; +using Content.Server.Flash.Components; +using Content.Server.Forensics; +using Content.Server.Temperature.Components; +using Content.Shared._Sunrise.CollectiveMind; +using Content.Shared._Sunrise.FleshCult; +using Content.Shared.Actions; +using Content.Shared.Body.Part; +using Content.Shared.Chemistry.Components; +using Content.Shared.Cuffs.Components; +using Content.Shared.Electrocution; +using Content.Shared.FixedPoint; +using Content.Shared.Hands.Components; +using Content.Shared.Humanoid; +using Content.Shared.Interaction.Components; +using Content.Shared.Inventory.Events; +using Content.Shared.Mobs; +using Content.Shared.Nutrition.Components; +using Content.Shared.Popups; +using Content.Shared.Store.Components; +using Content.Shared.Sunrise.CollectiveMind; +using Content.Shared.Tag; +using Robust.Shared.Audio; +using Robust.Shared.Containers; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server._Sunrise.FleshCult; + +public sealed partial class FleshCultSystem +{ + [ValidatePrototypeId] + private const string FleshCollectiveMindProto = "FleshCult"; + + [ValidatePrototypeId] + private const string FleshTagProto = "Flesh"; + + [ValidatePrototypeId] + private const string DefaultFleshCultRule = "FleshCult"; + + [ValidatePrototypeId] + private const string CreateFleshHeartObjective = "CreateFleshHeartObjective"; + + [ValidatePrototypeId] + private const string FleshCultSurviveObjective = "FleshCultSurviveObjective"; + + private void InitializeCultist() + { + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnInsulatedImmunityMutation); + SubscribeLocalEvent(OnPressureImmunityMutation); + SubscribeLocalEvent(OnFlashImmunityMutation); + SubscribeLocalEvent(OnRespiratorImmunityMutation); + SubscribeLocalEvent(OnColdTempImmunityMutation); + SubscribeLocalEvent(OnBeingEquippedAttempt); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnShop); + } + + private void OnShop(EntityUid uid, FleshCultistComponent component, FleshCultistShopActionEvent args) + { + if (!TryComp(uid, out var store)) + return; + _store.ToggleUi(uid, uid, store); + } + + private void OnMobStateChanged(EntityUid uid, FleshCultistComponent component, MobStateChangedEvent args) + { + switch (args.NewMobState) + { + case MobState.Critical: + { + EnsureComp(uid); + var hands = _handsSystem.EnumerateHands(uid); + var enumerateHands = hands as Hand[] ?? hands.ToArray(); + foreach (var enumerateHand in enumerateHands) + { + if (enumerateHand.Container == null) + continue; + foreach (var containerContainedEntity in enumerateHand.Container.ContainedEntities) + { + if (HasComp(containerContainedEntity)) + continue; + QueueDel(containerContainedEntity); + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + } + } + + break; + } + case MobState.Dead: + { + _inventory.TryGetSlotEntity(uid, "shoes", out var shoes); + if (shoes != null) + { + if (HasComp(shoes)) + { + EntityManager.DeleteEntity(shoes.Value); + _movement.RefreshMovementSpeedModifiers(uid); + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + } + } + + _inventory.TryGetSlotEntity(uid, "outerClothing", out var outerClothing); + if (outerClothing != null) + { + if (HasComp(outerClothing)) + { + EntityManager.DeleteEntity(outerClothing.Value); + _movement.RefreshMovementSpeedModifiers(uid); + _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); + } + } + + ParasiteComesOut(uid, component); + break; + } + } + } + + private void OnBeingEquippedAttempt(EntityUid uid, FleshCultistComponent component, IsEquippingAttemptEvent args) + { + if (args.Slot is not ("socks" or "outerClothing")) + return; + _inventory.TryGetSlotEntity(uid, "shoes", out var shoes); + if (shoes == null) + return; + if (HasComp(shoes)) + return; + if (args.Slot is "outerClothing" && !_tagSystem.HasTag(args.Equipment, "FullBodyOuter")) + return; + _popup.PopupEntity(Loc.GetString("flesh-cultist-equiped-outer-clothing-blocked", + ("Entity", uid)), uid, PopupType.Large); + args.Cancel(); + + } + + private void OnStartup(EntityUid uid, FleshCultistComponent component, ComponentStartup args) + { + ChangeParasiteHunger(uid, 0, component); + + if (TryComp(uid, out ActionsComponent? actionsComponent)) + { + _action.AddAction(uid, ref component.ActionFleshCultistShopEntity, component.ActionFleshCultistShop, component: actionsComponent); + var fleshAbilities = EnsureComp(uid); + var devourAction = _action.AddAction(uid, fleshAbilities.ActionFleshCultistDevourId, component: actionsComponent); + if (devourAction != null) + fleshAbilities.Actions.Add(devourAction.Value); + } + + var storeComp = EnsureComp(uid); + + var collectiveMindComponent = EnsureComp(uid); + if (!collectiveMindComponent.Minds.Contains(FleshCollectiveMindProto)) + collectiveMindComponent.Minds.Add(FleshCollectiveMindProto); + + storeComp.Categories.Add("FleshCultistPassiveSkills"); + storeComp.Categories.Add("FleshCultistActiveSkills"); + storeComp.Categories.Add("FleshCultistWeapon"); + storeComp.Categories.Add("FleshCultistArmor"); + storeComp.CurrencyWhitelist.Add("StolenMutationPoint"); + storeComp.BuySuccessSound = component.BuySuccesSound; + storeComp.RefundAllowed = false; + + EnsureComp(uid); + + if (HasComp(uid)) + RemComp(uid); + + if (HasComp(uid)) + RemComp(uid); + + _tagSystem.AddTag(uid, FleshTagProto); + + if (TryComp(uid, out var appearance)) + { + appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.RLeg); + appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.LLeg); + appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.RFoot); + appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.LFoot); + Dirty(uid, appearance); + } + } + + private void OnShutdown(EntityUid uid, FleshCultistComponent component, ComponentShutdown args) + { + if (TryComp(uid, out ActionsComponent? actionsComponent) && TryComp(uid, out FleshAbilitiesComponent? abilitiesComponent)) + { + _action.RemoveAction(uid, component.ActionFleshCultistShopEntity, comp: actionsComponent); + + foreach (var abilitiesComponentAction in abilitiesComponent.Actions) + { + _action.RemoveAction(uid, abilitiesComponentAction, comp: actionsComponent); + } + } + + RemCompDeferred(uid); + RemCompDeferred(uid); + RemCompDeferred(uid); + RemCompDeferred(uid); + RemCompDeferred(uid); + RemCompDeferred(uid); + + if (TryComp(uid, out CollectiveMindComponent? collectiveMind)) + { + if (collectiveMind.Minds.Contains(FleshCollectiveMindProto)) + collectiveMind.Minds.Remove(FleshCollectiveMindProto); + } + + EnsureComp(uid); + EnsureComp(uid); + + _alerts.ClearAlert(uid, component.MutationPointAlert); + + _tagSystem.RemoveTag(uid, FleshTagProto); + + if (_mindSystem.TryGetMind(uid, out var mindId, out var mind)) + { + _roles.MindRemoveRole((mindId, mind)); + + var indexesToRemove = new List(); + + for (var i = 0; i < mind.Objectives.Count; i++) + { + var mindObjective = mind.Objectives[i]; + var prototypeId = MetaData(mindObjective).EntityPrototype!.ID; + + if (prototypeId is CreateFleshHeartObjective || prototypeId is FleshCultSurviveObjective) + { + indexesToRemove.Add(i); + } + } + + for (var i = indexesToRemove.Count - 1; i >= 0; i--) + { + _mindSystem.TryRemoveObjective(mindId, mind, indexesToRemove[i]); + } + } + } + + private void OnInsulatedImmunityMutation(EntityUid uid, FleshCultistComponent component, + FleshCultistInsulatedImmunityMutationEvent args) + { + EnsureComp(uid); + } + + + private void OnPressureImmunityMutation(EntityUid uid, FleshCultistComponent component, + FleshCultistPressureImmunityMutationEvent args) + { + EnsureComp(uid); + } + + private void OnFlashImmunityMutation(EntityUid uid, FleshCultistComponent component, + FleshCultistFlashImmunityMutationEvent args) + { + EnsureComp(uid); + } + + private void OnRespiratorImmunityMutation(EntityUid uid, FleshCultistComponent component, + FleshCultistRespiratorImmunityMutationEvent args) + { + EnsureComp(uid); + } + + private void OnColdTempImmunityMutation(EntityUid uid, FleshCultistComponent component, + FleshCultistColdTempImmunityMutationEvent args) + { + if (TryComp(uid, out var tempComponent)) + { + tempComponent.ColdDamageThreshold = 0; + } + } + + private bool ChangeParasiteHunger(EntityUid uid, FixedPoint2 amount, FleshCultistComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + component.Hunger += amount; + + if (TryComp(uid, out var store)) + _store.UpdateUserInterface(uid, uid, store); + + _alerts.ShowAlert(uid, component.MutationPointAlert, (short) Math.Clamp(Math.Round(component.Hunger.Float() / 10f), 0, 16)); + + return true; + } + + private int MatchSaturation(int bloodVolume, bool hasAppearance) + { + if (hasAppearance) + { + return 80; + } + return bloodVolume switch + { + >= 300 => 60, + >= 150 => 40, + >= 100 => 20, + _ => 10 + }; + } + + private int MatchEvolutionPoint(int bloodVolume, bool hasAppearance) + { + if (hasAppearance) + { + return 20; + } + return bloodVolume switch + { + >= 300 => 15, + >= 150 => 10, + >= 100 => 5, + _ => 0 + }; + } + + private float MatchHealPoint(int bloodVolume, bool hasAppearance) + { + if (hasAppearance) + { + return 1; + } + return bloodVolume switch + { + >= 300 => 0.8f, + >= 150 => 0.6f, + >= 100 => 0.4f, + _ => 0.2f + }; + } + + private bool ParasiteComesOut(EntityUid uid, FleshCultistComponent? component = null) + { + if (!Resolve(uid, ref component)) + return false; + + var xform = Transform(uid); + var coordinates = xform.Coordinates; + + var abommob = Spawn(component.FleshMutationMobId, _transformSystem.GetMapCoordinates(uid)); + + if (_mindSystem.TryGetMind(uid, out var mindId, out var mind)) + { + _mindSystem.TransferTo(mindId, abommob, ghostCheckOverride: true); + } + + _popup.PopupEntity(Loc.GetString("flesh-pudge-transform-user", ("EntityTransform", uid)), + uid, uid, PopupType.LargeCaution); + _popup.PopupEntity(Loc.GetString("flesh-pudge-transform-others", + ("Entity", uid), ("EntityTransform", abommob)), abommob, Filter.PvsExcept(abommob), + true, PopupType.LargeCaution); + + _audioSystem.PlayPvs(component.SoundMutation, coordinates, AudioParams.Default.WithVariation(0.025f)); + + if (TryComp(uid, out ContainerManagerComponent? container)) + { + foreach (var cont in container.GetAllContainers().ToArray()) + { + foreach (var ent in cont.ContainedEntities.ToArray()) + { + if (HasComp(ent)) + continue; + if (HasComp(ent)) + continue; + _containerSystem.Remove(ent, cont, force: true); + Transform(ent).Coordinates = coordinates; + } + } + } + + if (TryComp(uid, out var bloodstream)) + { + var tempSol = new Solution() { MaxVolume = 5 }; + + if (bloodstream.BloodSolution == null) + return false; + + tempSol.AddSolution(bloodstream.BloodSolution.Value.Comp.Solution, _prototypeManager); + + if (_puddleSystem.TrySpillAt(uid, tempSol.SplitSolution(50), out var puddleUid)) + { + if (TryComp(uid, out var dna)) + { + var comp = EnsureComp(puddleUid); + comp.DNAs.Add(dna.DNA); + } + } + } + + QueueDel(uid); + return true; + } + + public void UpdateCultist(float frameTime) + { + base.Update(frameTime); + var curTime = _timing.CurTime; + + foreach (var rev in EntityQuery()) + { + rev.Accumulator += frameTime; + + if (rev.Accumulator <= 1) + continue; + rev.Accumulator -= 1; + + if (rev.Hunger <= 40) + { + rev.AccumulatorStarveNotify += 1; + if (rev.AccumulatorStarveNotify > 30) + { + rev.AccumulatorStarveNotify = 0; + _popup.PopupEntity(Loc.GetString("flesh-cultist-hungry"), + rev.Owner, rev.Owner, PopupType.Large); + } + } + + if (rev.Hunger < 0) + { + ParasiteComesOut(rev.Owner, rev); + } + + ChangeParasiteHunger(rev.Owner, rev.HungerСonsumption, rev); + } + } +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Heart.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Heart.cs new file mode 100644 index 0000000000..d5a0482acb --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Heart.cs @@ -0,0 +1,425 @@ +using System.Linq; +using System.Numerics; +using Content.Server._Sunrise.FleshCult.FleshGrowth; +using Content.Server._Sunrise.FleshCult.GameRule; +using Content.Server.Body.Components; +using Content.Server.Traits.Assorted; +using Content.Shared._Sunrise.FleshCult; +using Content.Shared.Body.Components; +using Content.Shared.Body.Organ; +using Content.Shared.Body.Part; +using Content.Shared.Damage; +using Content.Shared.Destructible; +using Content.Shared.DoAfter; +using Content.Shared.DragDrop; +using Content.Shared.Flesh; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Mind.Components; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Random.Helpers; +using Content.Shared.Tag; +using Robust.Shared.Audio; +using Robust.Shared.Containers; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Player; + +namespace Content.Server._Sunrise.FleshCult; + +public sealed partial class FleshCultSystem +{ + [Dependency] private readonly FleshCultRuleSystem _fleshCultRule = default!; + [Dependency] private readonly SharedPointLightSystem _pointLight = default!; + + public void InitializeHeart() + { + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnDestruction); + SubscribeLocalEvent(HandleDragDropOn); + SubscribeLocalEvent(OnDragFinished); + SubscribeLocalEvent(OnComponentInit); + } + + private void OnStartup(EntityUid uid, FleshHeartComponent component, ComponentStartup args) + { + _fleshCultRule.StartGameRule(); + var xform = Transform(uid); + RaiseLocalEvent(new FleshHeartStatusChangeEvent() + { + FleshHeartUid = uid, + OwningStation = xform.GridUid, + Status = FleshHeartStatus.Base + }); + } + + private void OnShutdown(EntityUid uid, FleshHeartComponent component, ComponentShutdown args) + { + _audioSystem.Stop(component.AmbientAudioStream); + } + + private void OnDestruction(EntityUid uid, FleshHeartComponent component, DestructionEventArgs args) + { + if (component.Status != HeartStatus.Base) + { + _audioSystem.Stop(component.AmbientAudioStream); + var xform = Transform(uid); + var coordinates = xform.Coordinates; + foreach (var ent in component.BodyContainer.ContainedEntities.ToArray()) + { + _containerSystem.Remove(ent, component.BodyContainer, force: true); + Transform(ent).Coordinates = coordinates; + } + var fleshTilesQuery = EntityQueryEnumerator(); + while (fleshTilesQuery.MoveNext(out var ent, out var comp)) + { + if (comp.Source != uid) + continue; + if (!TryComp(ent, out var tagComponent)) + continue; + if (_tagSystem.HasAllTags(tagComponent, "Wall", "Flesh")) + _damageableSystem.TryChangeDamage(ent, component.DamageMobsIfHeartDestruct); + else + QueueDel(ent); + + } + var fleshWalls = new List(); + var fleshWallsQuery = EntityQueryEnumerator(); + while (fleshWallsQuery.MoveNext(out var ent, out var comp)) + { + if (!TryComp(ent, out var tagComponent)) + continue; + var isFleshWall = _tagSystem.HasAllTags(tagComponent, "Wall", "Flesh"); + if (isFleshWall) + { + fleshWalls.Add(ent); + } + } + foreach (var mob in component.EdgeMobs.ToArray()) + { + _damageableSystem.TryChangeDamage(mob, component.DamageMobsIfHeartDestruct); + } + RaiseLocalEvent(new FleshHeartStatusChangeEvent() + { + FleshHeartUid = uid, + OwningStation = xform.GridUid, + Status = FleshHeartStatus.Destruction + }); + } + } + + public void UpdateHeart(float frameTime) + { + var fleshCultRule = EntityQuery().FirstOrDefault(); + if (fleshCultRule == null) + return; + + var fleshHeartQuery = EntityQueryEnumerator(); + while (fleshHeartQuery.MoveNext(out var ent, out var comp, out var xform)) + { + var owningStation = _stationSystem.GetOwningStation(ent, xform); + + if (owningStation != fleshCultRule.TargetStation) + continue; + + switch (comp.Status) + { + case HeartStatus.Base: + { + comp.Accumulator += frameTime; + + if (comp.Accumulator <= 1) + continue; + + comp.Accumulator -= 1; + if (comp.BodyContainer.ContainedEntities.Count >= comp.BodyToFinalStage) + { + comp.SpawnMobsAccumulator = 500; + comp.Status = HeartStatus.Active; + RaiseLocalEvent(new FleshHeartStatusChangeEvent() + { + FleshHeartUid = ent, + OwningStation = xform.GridUid, + Status = FleshHeartStatus.Active + }); + _pointLight.SetEnabled(ent, true); + _chatSystem.DispatchGlobalAnnouncement( + Loc.GetString("flesh-heart-activate-warning"), + colorOverride: Color.Red); + var stationUid = _stationSystem.GetOwningStation(ent); + SpawnFleshFloorOnOpenTiles(ent, comp, Transform(ent), 1); + _roundEndSystem.CancelRoundEndCountdown(stationUid); + _audioSystem.PlayPvs(comp.TransformSound, ent, comp.TransformSound.Params); + comp.AmbientAudioStream = _audioSystem.PlayGlobal( + "/Audio/_Sunrise/FleshCult/flesh_heart.ogg", Filter.Broadcast(), true, + AudioParams.Default.WithLoop(true).WithVolume(-3f))!.Value.Entity; + _sharedAppearance.SetData(ent, FleshHeartVisuals.State, FleshHeartStatus.Active); + } + break; + } + case HeartStatus.Active: + { + comp.SpawnMobsAccumulator += frameTime; + comp.SpawnObjectsAccumulator += frameTime; + comp.FinalStageAccumulator += frameTime; + if (comp.SpawnMobsAccumulator >= comp.SpawnMobsFrequency) + { + comp.SpawnMobsAccumulator = 0; + SpawnMonstersOnOpenTiles(comp, xform, comp.SpawnMobsAmount, comp.SpawnMobsRadius); + } + + if (comp.SpawnObjectsAccumulator >= comp.SpawnObjectsFrequency) + { + comp.SpawnObjectsAccumulator = 0; + // SpawnObjectsOnOpenTiles(comp, xform, comp.SpawnObjectsAmount, comp.SpawnObjectsRadius); + } + + if (comp.FinalStageAccumulator >= comp.TimeLiveFinalHeartToWin) + { + RaiseLocalEvent(new FleshHeartStatusChangeEvent() + { + FleshHeartUid = ent, + OwningStation = owningStation, + Status = FleshHeartStatus.Final + }); + comp.Status = HeartStatus.Disable; + } + break; + } + case HeartStatus.Disable: + { + break; + } + } + } + } + + #region Interaction + + private void HandleDragDropOn(EntityUid uid, FleshHeartComponent component, ref DragDropTargetEvent args) + { + + if (!CanAbsorb(uid, args.Dragged, component)) + { + _popup.PopupEntity(Loc.GetString("flesh-heart-cant-absorb-targer"), + args.User, PopupType.Large); + return; + } + + if (!TryComp(args.Dragged, out var fixturesComponent)) + { + _popup.PopupEntity(Loc.GetString("flesh-heart-cant-absorb-targer"), + args.User, PopupType.Large); + return; + } + + if (fixturesComponent.Fixtures["fix1"].Density <= 60) + { + _popup.PopupEntity( + Loc.GetString("flesh-heart-cant-absorb-targer"), + uid, PopupType.Large); + return; + } + + var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.EntryDelay, new FleshHeartDragFinished(), uid, target: args.Dragged, used: uid) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = false, + }; + _doAfterSystem.TryStartDoAfter(doAfterArgs); + args.Handled = true; + } + + private void OnComponentInit(EntityUid uid, FleshHeartComponent cryoPodComponent, ComponentInit args) + { + cryoPodComponent.BodyContainer = _containerSystem.EnsureContainer(uid, "bodyContainer"); + } + + private void OnDragFinished(EntityUid uid, FleshHeartComponent component, FleshHeartDragFinished args) + { + if (args.Cancelled || args.Handled || args.Args.Target == null) + return; + + if (!TryComp(args.Args.Target.Value, out var fixturesComponent)) + { + _popup.PopupEntity(Loc.GetString("flesh-heart-cant-absorb-targer"), + args.User, PopupType.Large); + return; + } + + var xform = Transform(args.Args.Target.Value); + + if (TryComp(args.Args.Target.Value, out ContainerManagerComponent? container)) + { + foreach (var cont in container.GetAllContainers().ToArray()) + { + foreach (var ent in cont.ContainedEntities.ToArray()) + { + { + if (HasComp(ent)) + { + continue; + } + _containerSystem.Remove(ent, cont, force: true); + Transform(ent).Coordinates = xform.Coordinates; + } + } + } + } + + // SUNRISE-TODO: Убрать конечности хирургией а тело заменить на скелета + if (TryComp(args.Args.Target.Value, out var HuAppComponent)) + { + if (TryComp(args.Args.Target.Value, out var bloodstreamComponent)) + _bloodstreamSystem.TryModifyBloodLevel(args.Args.Target.Value, -300, bloodstreamComponent); + + if (TryComp(args.Args.Target.Value, out var bodyComponent)) + { + var parts = _body.GetBodyChildren(args.Args.Target.Value, bodyComponent).ToArray(); + + foreach (var part in parts) + { + if (part.Component.PartType == BodyPartType.Head) + continue; + + if (part.Component.PartType == BodyPartType.Torso) + { + foreach (var organ in _body.GetPartOrgans(part.Id, part.Component)) + { + //_body.RemoveOrgan(organ.Id); + QueueDel(organ.Id); + } + } + else + { + QueueDel(part.Id); + } + } + } + + var skeletonSprites = _prototypeManager.Index("MobSkeletonSprites"); + foreach (var (key, id) in skeletonSprites.Sprites) + { + if (key != HumanoidVisualLayers.Head) + { + _sharedHuApp.SetBaseLayerId(args.Args.Target.Value, key, id, humanoid: HuAppComponent); + } + } + + _physics.SetDensity(args.Args.Target.Value, "fix1", fixturesComponent.Fixtures["fix1"], 50); + + if (TryComp(args.Args.Target.Value, out var appComponent)) + { + _sharedAppearance.SetData(args.Args.Target.Value, DamageVisualizerKeys.Disabled, true, appComponent); + _damageableSystem.TryChangeDamage(args.Args.Target.Value, + new DamageSpecifier() { DamageDict = { { "Slash", 100 } } }); + } + + EnsureComp(args.Args.Target.Value); + + _containerSystem.Insert(args.Args.Target.Value, component.BodyContainer, force: true); + _audioSystem.PlayPvs(component.TransformSound, uid, component.TransformSound.Params); + } + + args.Handled = true; + } + + #endregion + + private bool CanAbsorb(EntityUid uid, EntityUid dragged, FleshHeartComponent component) + { + if (!TryComp(dragged, out var stateComponent)) + return false; + + if (stateComponent.CurrentState != MobState.Dead) + return false; + + if (!Transform(uid).Anchored) + return false; + + if (!TryComp(dragged, out var humanoidAppearance)) + return false; + + if (!_speciesWhitelist.Contains(humanoidAppearance.Species)) + return false; + + return !TryComp(dragged, out var mindComp) || true; + } + + private void SpawnFleshFloorOnOpenTiles(EntityUid fleshHeart, FleshHeartComponent component, TransformComponent xform, float radius) + { + if (!TryComp(xform.GridUid, out var grid)) + return; + + var localpos = xform.Coordinates.Position; + var tilerefs = grid.GetLocalTilesIntersecting( + new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius))).ToArray(); + foreach (var tileref in tilerefs) + { + var canSpawnFloor = true; + foreach (var ent in grid.GetAnchoredEntities(tileref.GridIndices).ToList()) + { + if (_tagSystem.HasAnyTag(ent, "Wall", "Window", "Flesh")) + canSpawnFloor = false; + } + if (canSpawnFloor) + { + var location = _mapSystem.ToCenterCoordinates(tileref, grid); + var fleshTile = EntityManager.SpawnEntity(component.FleshTileId, location); + var spreaderFleshComponent = EnsureComp(fleshTile); + spreaderFleshComponent.Source = fleshHeart; + } + } + } + + private void SpawnMonstersOnOpenTiles(FleshHeartComponent component, TransformComponent xform, int amount, float radius) + { + if (!TryComp(xform.GridUid, out var grid)) + return; + + var localpos = xform.Coordinates.Position; + var tilerefs = grid.GetLocalTilesIntersecting( + new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius))).ToArray(); + _random.Shuffle(tilerefs); + var physQuery = GetEntityQuery(); + var amountCounter = 0; + foreach (var tileref in tilerefs) + { + var valid = true; + foreach (var ent in grid.GetAnchoredEntities(tileref.GridIndices)) + { + if (!physQuery.TryGetComponent(ent, out var body)) + continue; + if (body.BodyType != BodyType.Static || + !body.Hard || + (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0) + continue; + valid = false; + break; + } + if (!valid) + continue; + amountCounter++; + + var randomMob = _random.Pick(component.Spawns); + var location = _mapSystem.ToCenterCoordinates(tileref, grid); + var mob = Spawn(randomMob, location); + component.EdgeMobs.Add(mob); + if (amountCounter >= amount) + return; + } + } + + public sealed class FleshHeartStatusChangeEvent : EntityEventArgs + { + public EntityUid FleshHeartUid; + public EntityUid? OwningStation; + public FleshHeartStatus Status; + } +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Hugger.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Hugger.cs new file mode 100644 index 0000000000..acc0c3b6dd --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Hugger.cs @@ -0,0 +1,257 @@ +using System.Linq; +using Content.Server.Nutrition.Components; +using Content.Shared._Sunrise.FleshCult; +using Content.Shared.CombatMode.Pacification; +using Content.Shared.Eye.Blinding.Components; +using Content.Shared.Hands; +using Content.Shared.Humanoid; +using Content.Shared.IdentityManagement.Components; +using Content.Shared.Inventory.Events; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Popups; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Melee.Events; +using Robust.Shared.Player; + +namespace Content.Server._Sunrise.FleshCult; + +public sealed partial class FleshCultSystem +{ + public void InitializeHugger() + { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnMeleeHit); + SubscribeLocalEvent(OnWormDoHit); + SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnequipped); + SubscribeLocalEvent(OnGotEquippedHand); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnJump); + SubscribeLocalEvent(OnGetOff); + } + + private void OnMapInit(EntityUid uid, FleshHuggerComponent component, MapInitEvent args) + { + _action.AddAction(uid, component.ActionFleshHuggerJumpId); + _action.AddAction(uid, component.ActionFleshHuggerGetOffId); + } + + private void OnWormDoHit(EntityUid uid, FleshHuggerComponent component, ThrowDoHitEvent args) + { + if (component.IsDeath) + return; + if (HasComp(args.Target)) + return; + if (!HasComp(args.Target)) + return; + if (TryComp(args.Target, out MobStateComponent? mobState)) + { + if (mobState.CurrentState is not MobState.Alive) + { + return; + } + } + _inventory.TryGetSlotEntity(args.Target, "head", out var headItem); + if (HasComp(headItem)) + return; + + _inventory.TryGetSlotEntity(args.Target, "mask", out var maskItem); + if (HasComp(maskItem)) + return; + + _inventory.TryUnequip(args.Target, "head", true); + _inventory.TryUnequip(args.Target, "eyes", true); + _inventory.TryUnequip(args.Target, "mask", true); + + var equipped = _inventory.TryEquip(args.Target, uid, "mask", true); + if (!equipped) + return; + + component.EquipedOn = args.Target; + + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-user"), + args.Target, args.Target, PopupType.LargeCaution); + + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-mob", + ("entity", args.Target)), + uid, uid, PopupType.LargeCaution); + + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-others", + ("entity", args.Target)), args.Target, Filter.PvsExcept(uid), true, PopupType.Large); + + EntityManager.EnsureComponent(uid); + _stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(component.ParalyzeTime), true); + _damageableSystem.TryChangeDamage(args.Target, component.Damage, origin: args.Thrown); + } + + private void OnGotEquipped(EntityUid uid, FleshHuggerComponent component, GotEquippedEvent args) + { + if (args.Slot != "mask") + return; + component.EquipedOn = args.Equipee; + EntityManager.EnsureComponent(args.Equipee); + EntityManager.EnsureComponent(uid); + } + + private void OnGotEquippedHand(EntityUid uid, FleshHuggerComponent component, GotEquippedHandEvent args) + { + if (HasComp(args.User)) + return; + if (component.IsDeath) + return; + _damageableSystem.TryChangeDamage(args.User, component.Damage); + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-bite-user"), + args.User, args.User); + } + + private void OnGotUnequipped(EntityUid uid, FleshHuggerComponent component, GotUnequippedEvent args) + { + if (args.Slot != "mask") + return; + if (HasComp(uid)) + EntityManager.RemoveComponent(uid); + if (HasComp(component.EquipedOn)) + EntityManager.RemoveComponent(args.Equipee); + _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(3), true); + component.EquipedOn = new EntityUid(); + } + + private void OnMeleeHit(EntityUid uid, FleshHuggerComponent component, MeleeHitEvent args) + { + if (!args.HitEntities.Any()) + return; + + foreach (var entity in args.HitEntities) + { + if (!HasComp(entity)) + return; + + if (TryComp(entity, out MobStateComponent? mobState)) + { + if (mobState.CurrentState is not MobState.Alive) + { + return; + } + } + + _inventory.TryGetSlotEntity(entity, "head", out var headItem); + if (HasComp(headItem)) + return; + + _inventory.TryGetSlotEntity(entity, "mask", out var maskItem); + if (HasComp(maskItem)) + return; + + var random = new Random(); + var shouldEquip = random.Next(1, 101) <= component.ChansePounce; + if (!shouldEquip) + return; + + _inventory.TryUnequip(entity, "head", true); + _inventory.TryUnequip(entity, "eyes", true); + _inventory.TryUnequip(entity, "mask", true); + + var equipped = _inventory.TryEquip(entity, uid, "mask", true); + if (!equipped) + return; + + component.EquipedOn = entity; + + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-user"), + entity, entity, PopupType.LargeCaution); + + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-mob", ("entity", entity)), + uid, uid, PopupType.LargeCaution); + + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-others", + ("entity", entity)), entity, Filter.PvsExcept(entity), true, PopupType.Large); + EntityManager.EnsureComponent(uid); + _stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(component.ParalyzeTime), true); + _damageableSystem.TryChangeDamage(entity, component.Damage, origin: entity); + break; + } + } + + private static void OnMobStateChanged(EntityUid uid, FleshHuggerComponent component, MobStateChangedEvent args) + { + if (args.NewMobState == MobState.Dead) + { + component.IsDeath = true; + } + } + + private void OnGetOff(EntityUid uid, FleshHuggerComponent component, FleshHuggerGetOffFromFaceActionEvent args) + { + if (args.Handled) + return; + + if (component.EquipedOn is not { Valid: true } targetId) + { + _popup.PopupEntity(Loc.GetString("flesh-worm-cant-get-off"), + uid, uid, PopupType.LargeCaution); + return; + } + + _inventory.TryUnequip(targetId, "mask", true, true); + component.EquipedOn = new EntityUid(); + + args.Handled = true; + } + + private void OnJump(EntityUid uid, FleshHuggerComponent component, FleshHuggerJumpActionEvent args) + { + if (args.Handled) + return; + + if (component.EquipedOn is { Valid: true }) + { + _popup.PopupEntity(Loc.GetString("flesh-worm-cant-jump"), + uid, uid, PopupType.LargeCaution); + return; + } + + args.Handled = true; + var xform = Transform(uid); + var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); + var direction = mapCoords.Position - xform.MapPosition.Position; + + _throwing.TryThrow(uid, direction, 7F, uid, 10F); + if (component.SoundJump != null) + { + _audioSystem.PlayPvs(component.SoundJump, uid, component.SoundJump.Params); + } + } + + public void UpdateHugger(float frameTime) + { + foreach (var comp in EntityQuery()) + { + comp.Accumulator += frameTime; + + if (comp.Accumulator <= comp.DamageFrequency) + continue; + + comp.Accumulator = 0; + + if (comp.EquipedOn is not { Valid: true } targetId) + continue; + if (HasComp(comp.EquipedOn)) + return; + if (TryComp(targetId, out MobStateComponent? mobState)) + { + if (mobState.CurrentState is not MobState.Alive) + { + _inventory.TryUnequip(targetId, "mask", true, true); + comp.EquipedOn = new EntityUid(); + return; + } + } + _damageableSystem.TryChangeDamage(targetId, comp.Damage); + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-user"), + targetId, targetId, PopupType.LargeCaution); + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-others", + ("entity", targetId)), targetId, Filter.PvsExcept(targetId), true); + } + } +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Mob.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Mob.cs new file mode 100644 index 0000000000..0271c6cbc9 --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Mob.cs @@ -0,0 +1,29 @@ +using Content.Shared._Sunrise.FleshCult; +using Content.Shared.Mobs; + +namespace Content.Server._Sunrise.FleshCult; + +public sealed partial class FleshCultSystem +{ + public void InitializeMob() + { + SubscribeLocalEvent(OnMobStateChanged); + } + + private void OnMobStateChanged(EntityUid uid, FleshMobComponent component, MobStateChangedEvent args) + { + if (args.NewMobState != MobState.Dead) + return; + if (component.SoundDeath == null) + return; + _audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params); + + var coords = _transformSystem.GetMapCoordinates(uid); + + for (var i = 0; i < component.DeathMobSpawnCount; i++) + { + Spawn(component.DeathMobSpawnId, coords); + } + } +} + diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Virus.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Virus.cs new file mode 100644 index 0000000000..b051934804 --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.Virus.cs @@ -0,0 +1,120 @@ +using Content.Server._Sunrise.FleshCult.GameRule; +using Content.Server.Sunrise.FleshCult; +using Content.Shared.Humanoid; +using Content.Shared.Mind.Components; +using Content.Shared.Mindshield.Components; +using Content.Shared.Popups; +using Robust.Shared.Player; + +namespace Content.Server._Sunrise.FleshCult; + +public sealed partial class FleshCultSystem +{ + private void InitializeVirus() + { + SubscribeLocalEvent(OnPendingMapInit); + } + + private void OnPendingMapInit(EntityUid uid, PendingFleshCultistComponent component, MapInitEvent args) + { + component.NextParalyze = _timing.CurTime + TimeSpan.FromSeconds(1f); + component.NextScream = _timing.CurTime + TimeSpan.FromSeconds(1f); + } + + public void UpdateVirus(float frameTime) + { + var curTime = _timing.CurTime; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.CurrentStage == PendingFleshCultistStage.Final) + continue; + + comp.Accumulator += frameTime; + + var stageTimer = comp.CurrentStage switch + { + PendingFleshCultistStage.First => comp.FirstStageTimer, + PendingFleshCultistStage.Second => comp.SecondStageTimer, + _ => 0f + }; + + if (comp.Accumulator >= stageTimer) + { + comp.Accumulator = 0f; + comp.CurrentStage = GetNextStage(comp.CurrentStage); + } + + switch (comp.CurrentStage) + { + case PendingFleshCultistStage.First: + if (comp.NextScream <= curTime) + { + comp.NextScream = curTime + TimeSpan.FromSeconds(comp.ScreamInterval); + _chatSystem.TryEmoteWithChat(uid, "Scream"); + } + if (comp.NextStutter <= curTime) + { + comp.NextStutter = curTime + TimeSpan.FromSeconds(comp.ScreamInterval); + _stuttering.DoStutter(uid, TimeSpan.FromSeconds(comp.StutterTime), true); + } + break; + case PendingFleshCultistStage.Second: + if (comp.NextParalyze <= curTime) + { + comp.NextParalyze = curTime + TimeSpan.FromSeconds(comp.ParalyzeInterval); + _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(comp.ParalyzeTime), true); + } + if (comp.NextJitter <= curTime) + { + comp.NextJitter = curTime + TimeSpan.FromSeconds(comp.JitterInterval); + _jittering.DoJitter(uid, TimeSpan.FromSeconds(comp.JitterTime), true); + } + break; + case PendingFleshCultistStage.Third: + { + if (!HasComp(uid) || !TryComp(uid, out var targetActor)) + return; + + var targetPlayer = targetActor.PlayerSession; + + if (HasComp(uid)) + { + // SUNRISE-TODO: Сделать это внутри системы майншилда + _popup.PopupEntity("Активация самоуничтожения импланта защиты разума", uid, PopupType.LargeCaution); + _body.GibBody(uid, true); + _explosionSystem.QueueExplosion(uid, "Default", 50, 5, 30, canCreateVacuum: false); + break; + } + + if (!TryComp(uid, out var humanoidAppearance)) + break; + + if (_speciesWhitelist.Contains(humanoidAppearance.Species)) + { + RemCompDeferred(uid); + break; + } + + _antag.ForceMakeAntag(targetPlayer, DefaultFleshCultRule); + + comp.CurrentStage = PendingFleshCultistStage.Final; + RemCompDeferred(uid); + break; + } + } + } + } + + private PendingFleshCultistStage GetNextStage(PendingFleshCultistStage currentStage) + { + return currentStage switch + { + PendingFleshCultistStage.First => PendingFleshCultistStage.Second, + PendingFleshCultistStage.Second => PendingFleshCultistStage.Third, + PendingFleshCultistStage.Third => PendingFleshCultistStage.Final, + _ => currentStage, + }; + } +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs new file mode 100644 index 0000000000..70d1cd88f9 --- /dev/null +++ b/Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs @@ -0,0 +1,118 @@ +using Content.Server.Actions; +using Content.Server.Antag; +using Content.Server.Body.Systems; +using Content.Server.Chat.Systems; +using Content.Server.Cuffs; +using Content.Server.Explosion.EntitySystems; +using Content.Server.Fluids.EntitySystems; +using Content.Server.Humanoid; +using Content.Server.Mind; +using Content.Server.Popups; +using Content.Server.RoundEnd; +using Content.Server.Station.Systems; +using Content.Server.Store.Systems; +using Content.Server.Weapons.Ranged.Systems; +using Content.Shared.Alert; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Inventory; +using Content.Shared.Jittering; +using Content.Shared.Movement.Systems; +using Content.Shared.Roles; +using Content.Shared.Speech.EntitySystems; +using Content.Shared.Stunnable; +using Content.Shared.Tag; +using Content.Shared.Throwing; +using Robust.Server.Containers; +using Robust.Server.GameObjects; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server._Sunrise.FleshCult; + +public sealed partial class FleshCultSystem : EntitySystem +{ + [Dependency] private readonly ActionsSystem _action = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly StoreSystem _store = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly PuddleSystem _puddleSystem = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly HumanoidAppearanceSystem _sharedHuApp = default!; + [Dependency] private readonly SharedAppearanceSystem _sharedAppearance = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; + [Dependency] private readonly GunSystem _gunSystem = default!; + [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly CuffableSystem _cuffable = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedStunSystem _stunSystem = default!; + [Dependency] private readonly ChatSystem _chatSystem = default!; + [Dependency] private readonly SharedJitteringSystem _jittering = default!; + [Dependency] private readonly SharedStutteringSystem _stuttering = default!; + [Dependency] private readonly ExplosionSystem _explosionSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; + [Dependency] private readonly ContainerSystem _containerSystem = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly StationSystem _stationSystem = default!; + [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MapSystem _mapSystem = default!; + + private readonly List _speciesWhitelist = + [ + "Human", + "Reptilian", + "Dwarf", + "Vulpkanin", + "Felinid", + "Moth", + "Swine", + "Arachnid", + "Demon", + "Vox", + "HumanoidXeno", + "Predator", + "Tajaran" + ]; + + public override void Initialize() + { + base.Initialize(); + + InitializeVirus(); + InitializeAbilities(); + InitializeCultist(); + InitializeMob(); + InitializeHugger(); + InitializeHeart(); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + UpdateCultist(frameTime); + UpdateHugger(frameTime); + UpdateVirus(frameTime); + UpdateHeart(frameTime); + } +} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultistSystem.Abilities.cs b/Content.Server/_Sunrise/FleshCult/FleshCultistSystem.Abilities.cs deleted file mode 100644 index b3005d5751..0000000000 --- a/Content.Server/_Sunrise/FleshCult/FleshCultistSystem.Abilities.cs +++ /dev/null @@ -1,644 +0,0 @@ -using System.Linq; -using System.Numerics; -using Content.Server.Construction.Components; -using Content.Shared._Sunrise.FleshCult; -using Content.Shared.Chemistry.Components; -using Content.Shared.Coordinates.Helpers; -using Content.Shared.Cuffs.Components; -using Content.Shared.Hands.Components; -using Content.Shared.Maps; -using Content.Shared.Mobs.Components; -using Content.Shared.Physics; -using Content.Shared.Popups; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Components; -using Robust.Shared.Player; - -namespace Content.Server._Sunrise.FleshCult; - -public sealed partial class FleshCultistSystem -{ - private void InitializeAbilities() - { - SubscribeLocalEvent(OnBladeActionEvent); - SubscribeLocalEvent(OnClawActionEvent); - SubscribeLocalEvent(OnFistActionEvent); - SubscribeLocalEvent(OnSpikeHandGunActionEvent); - SubscribeLocalEvent(OnArmorActionEvent); - SubscribeLocalEvent(OnHeavyArmorActionEvent); - SubscribeLocalEvent(OnSpiderLegsActionEvent); - SubscribeLocalEvent(OnAdrenalinActionEvent); - SubscribeLocalEvent(OnCreateFleshHeartActionEvent); - SubscribeLocalEvent(OnThrowHugger); - SubscribeLocalEvent(OnAcidSpit); - } - - private void OnAcidSpit(EntityUid uid, FleshCultistComponent component, FleshCultistAcidSpitActionEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - var acidBullet = Spawn(component.BulletAcidSpawnId, Transform(uid).Coordinates); - var xform = Transform(uid); - var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); - var direction = mapCoords.Position - xform.MapPosition.Position; - var userVelocity = _physics.GetMapLinearVelocity(uid); - - _gunSystem.ShootProjectile(acidBullet, direction, userVelocity, uid, uid); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - } - - private void OnBladeActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistBladeActionEvent args) - { - if (args.Handled) - return; - if (TryComp(uid, out var cuffableComponent)) - { - if (cuffableComponent.CuffedHandCount > 0) - _cuffable.Uncuff(uid, uid, cuffableComponent.LastAddedCuffs); - } - var hands = _handsSystem.EnumerateHands(uid); - var enumerateHands = hands as Hand[] ?? Enumerable.ToArray(hands); - foreach (var enumerateHand in enumerateHands) - { - if (enumerateHand.Container == null) - continue; - foreach (var containerContainedEntity in enumerateHand.Container.ContainedEntities) - { - if (!TryComp(containerContainedEntity, out MetaDataComponent? metaData)) - continue; - if (metaData.EntityPrototype == null) - continue; - if (!HasComp<_Sunrise.FleshCult.FleshHandModComponent>(containerContainedEntity)) - { - if (enumerateHand != enumerateHands.First()) - continue; - var isDrop = _handsSystem.TryDrop(uid, checkActionBlocker: false); - if (metaData.EntityPrototype.ID == component.BladeSpawnId) - continue; - if (isDrop) - continue; - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - return; - } - { - if (enumerateHand != enumerateHands.First()) - { - if (metaData.EntityPrototype.ID != component.BladeSpawnId) - continue; - QueueDel(containerContainedEntity); - } - else - { - if (metaData.EntityPrototype.ID != component.BladeSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - } - else - { - QueueDel(containerContainedEntity); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-blade-in-hand", - ("Entity", uid)), uid, PopupType.LargeCaution); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - EnsureComp(uid); - args.Handled = true; - } - - return; - } - } - } - } - - var blade = Spawn(component.BladeSpawnId, Transform(uid).Coordinates); - var isPickup = _handsSystem.TryPickup(uid, blade, checkActionBlocker: false, - animateUser: false, animate: false); - if (isPickup) - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-hand-in-blade", ("Entity", uid)), - uid, PopupType.LargeCaution); - if (HasComp(uid)) - { - EntityManager.RemoveComponent(uid); - } - } - else - { - Logger.Error("Failed to equip blade to hand, removing blade"); - QueueDel(blade); - } - args.Handled = true; - } - - private void OnClawActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistClawActionEvent args) - { - if (args.Handled) - return; - if (TryComp(uid, out var cuffableComponent)) - { - if (cuffableComponent.CuffedHandCount > 0) - _cuffable.Uncuff(uid, uid, cuffableComponent.LastAddedCuffs); - } - var hands = _handsSystem.EnumerateHands(uid); - var enumerateHands = hands as Hand[] ?? Enumerable.ToArray(hands); - foreach (var enumerateHand in enumerateHands) - { - if (enumerateHand.Container == null) - continue; - foreach (var containerContainedEntity in enumerateHand.Container.ContainedEntities) - { - if (!TryComp(containerContainedEntity, out MetaDataComponent? metaData)) - continue; - if (metaData.EntityPrototype == null) - continue; - if (!HasComp<_Sunrise.FleshCult.FleshHandModComponent>(containerContainedEntity)) - { - if (enumerateHand != enumerateHands.First()) - continue; - var isDrop = _handsSystem.TryDrop(uid, checkActionBlocker: false); - if (metaData.EntityPrototype.ID == component.ClawSpawnId) - continue; - if (isDrop) - continue; - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - return; - } - { - if (enumerateHand != enumerateHands.First()) - { - if (metaData.EntityPrototype.ID != component.ClawSpawnId) - continue; - QueueDel(containerContainedEntity); - } - else - { - if (metaData.EntityPrototype.ID != component.ClawSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - } - else - { - QueueDel(containerContainedEntity); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-claw-in-hand", - ("Entity", uid)), uid, PopupType.LargeCaution); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - EnsureComp(uid); - args.Handled = true; - } - return; - } - } - } - } - - var claw = Spawn(component.ClawSpawnId, Transform(uid).Coordinates); - var isPickup = _handsSystem.TryPickup(uid, claw, checkActionBlocker: false, - animateUser: false, animate: false); - if (isPickup) - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-hand-in-claw", ("Entity", uid)), - uid, PopupType.LargeCaution); - if (HasComp(uid)) - EntityManager.RemoveComponent(uid); - } - else - { - QueueDel(claw); - } - args.Handled = true; - } - - - private void OnFistActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistFistActionEvent args) - { - if (args.Handled) - return; - if (TryComp(uid, out var cuffableComponent)) - { - if (cuffableComponent.CuffedHandCount > 0) - _cuffable.Uncuff(uid, uid, cuffableComponent.LastAddedCuffs); - } - var hands = _handsSystem.EnumerateHands(uid); - var enumerateHands = hands as Hand[] ?? Enumerable.ToArray(hands); - foreach (var enumerateHand in enumerateHands) - { - if (enumerateHand.Container == null) - continue; - foreach (var containerContainedEntity in enumerateHand.Container.ContainedEntities) - { - if (!TryComp(containerContainedEntity, out MetaDataComponent? metaData)) - continue; - if (metaData.EntityPrototype == null) - continue; - if (!HasComp<_Sunrise.FleshCult.FleshHandModComponent>(containerContainedEntity)) - { - if (enumerateHand != enumerateHands.First()) - continue; - var isDrop = _handsSystem.TryDrop(uid, checkActionBlocker: false); - if (metaData.EntityPrototype.ID == component.FistSpawnId) - continue; - if (isDrop) - continue; - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - return; - } - { - if (enumerateHand != enumerateHands.First()) - { - if (metaData.EntityPrototype.ID != component.FistSpawnId) - continue; - QueueDel(containerContainedEntity); - } - else - { - if (metaData.EntityPrototype.ID != component.FistSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - } - else - { - QueueDel(containerContainedEntity); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-claw-in-hand", - ("Entity", uid)), uid, PopupType.LargeCaution); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - EnsureComp(uid); - args.Handled = true; - } - return; - } - } - } - } - - var fist = Spawn(component.FistSpawnId, Transform(uid).Coordinates); - var isPickup = _handsSystem.TryPickup(uid, fist, checkActionBlocker: false, - animateUser: false, animate: false); - if (isPickup) - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-hand-in-claw", ("Entity", uid)), - uid, PopupType.LargeCaution); - if (HasComp(uid)) - EntityManager.RemoveComponent(uid); - } - else - { - QueueDel(fist); - } - args.Handled = true; - } - - private void OnSpikeHandGunActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistSpikeHandGunActionEvent args) - { - if (args.Handled) - return; - if (TryComp(uid, out var cuffableComponent)) - { - if (cuffableComponent.CuffedHandCount > 0) - _cuffable.Uncuff(uid, uid, cuffableComponent.LastAddedCuffs); - } - var hands = _handsSystem.EnumerateHands(uid); - var enumerateHands = hands as Hand[] ?? Enumerable.ToArray(hands); - foreach (var enumerateHand in enumerateHands) - { - if (enumerateHand.Container == null) - continue; - foreach (var containerContainedEntity in enumerateHand.Container.ContainedEntities) - { - if (!TryComp(containerContainedEntity, out MetaDataComponent? metaData)) - continue; - if (metaData.EntityPrototype == null) - continue; - if (!HasComp<_Sunrise.FleshCult.FleshHandModComponent>(containerContainedEntity)) - { - if (enumerateHand != enumerateHands.First()) - continue; - var isDrop = _handsSystem.TryDrop(uid, checkActionBlocker: false); - if (metaData.EntityPrototype.ID == component.SpikeHandGunSpawnId) - continue; - if (isDrop) - continue; - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - return; - } - { - if (enumerateHand != enumerateHands.First()) - { - if (metaData.EntityPrototype.ID != component.SpikeHandGunSpawnId) - continue; - QueueDel(containerContainedEntity); - } - else - { - if (metaData.EntityPrototype.ID != component.SpikeHandGunSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-user-hand-blocked"), - uid, uid, PopupType.Large); - } - else - { - QueueDel(containerContainedEntity); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-spike-gun-in-hand", - ("Entity", uid)), uid, PopupType.LargeCaution); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - EnsureComp(uid); - args.Handled = true; - } - return; - } - } - } - } - - var claw = Spawn(component.SpikeHandGunSpawnId, Transform(uid).Coordinates); - var isPickup = _handsSystem.TryPickup(uid, claw, checkActionBlocker: false, - animateUser: false, animate: false); - if (isPickup) - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-hand-in-spike-gun", ("Entity", uid)), - uid, PopupType.LargeCaution); - if (HasComp(uid)) - RemComp(uid); - } - else - { - QueueDel(claw); - } - args.Handled = true; - } - - private void OnArmorActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistArmorActionEvent args) - { - _inventory.TryGetSlotEntity(uid, "outerClothing", out var outerClothing); - if (outerClothing != null) - { - if (!TryComp(outerClothing, out MetaDataComponent? metaData)) - return; - if (metaData.EntityPrototype == null) - return; - if (metaData.EntityPrototype.ID == component.HeavyArmorSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-blocked"), - uid, uid, PopupType.Large); - } - else if (metaData.EntityPrototype.ID != component.ArmorSpawnId) - { - _inventory.TryUnequip(uid, "outerClothing", true, true); - var armor = Spawn(component.ArmorSpawnId, Transform(uid).Coordinates); - var equipped = _inventory.TryEquip(uid, armor, "outerClothing", true); - if (!equipped) - { - QueueDel(armor); - } - else - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-on", - ("Entity", uid)), uid, PopupType.LargeCaution); - args.Handled = true; - } - } - else - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-off", - ("Entity", uid)), uid, PopupType.LargeCaution); - EntityManager.DeleteEntity(outerClothing.Value); - _movement.RefreshMovementSpeedModifiers(uid); - args.Handled = true; - } - } - else - { - var armor = Spawn(component.ArmorSpawnId, Transform(uid).Coordinates); - var equipped = _inventory.TryEquip(uid, armor, "outerClothing", true); - if (!equipped) - { - QueueDel(armor); - } - else - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-on", - ("Entity", uid)), uid, PopupType.LargeCaution); - args.Handled = true; - } - } - } - - private void OnHeavyArmorActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistHeavyArmorActionEvent args) - { - _inventory.TryGetSlotEntity(uid, "shoes", out var shoes); - if (shoes != null) - { - if (!TryComp(shoes, out MetaDataComponent? metaData)) - return; - if (metaData.EntityPrototype == null) - return; - if (metaData.EntityPrototype.ID == component.SpiderLegsSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-blocked"), - uid, uid, PopupType.Large); - return; - } - } - _inventory.TryGetSlotEntity(uid, "outerClothing", out var outerClothing); - if (outerClothing != null) - { - if (!TryComp(outerClothing, out MetaDataComponent? metaData)) - return; - if (metaData.EntityPrototype == null) - return; - if (metaData.EntityPrototype.ID == component.ArmorSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-blocked"), - uid, uid, PopupType.Large); - } - else if (metaData.EntityPrototype.ID != component.HeavyArmorSpawnId) - { - _inventory.TryUnequip(uid, "outerClothing", true, true); - var armor = Spawn(component.HeavyArmorSpawnId, Transform(uid).Coordinates); - var equipped = _inventory.TryEquip(uid, armor, "outerClothing", true); - if (!equipped) - { - QueueDel(armor); - } - else - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-on", - ("Entity", uid)), uid, PopupType.LargeCaution); - args.Handled = true; - } - } - else - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-off", - ("Entity", uid)), uid, PopupType.LargeCaution); - EntityManager.DeleteEntity(outerClothing.Value); - _movement.RefreshMovementSpeedModifiers(uid); - args.Handled = true; - } - } - else - { - var armor = Spawn(component.HeavyArmorSpawnId, Transform(uid).Coordinates); - var equipped = _inventory.TryEquip(uid, armor, "outerClothing", true); - if (!equipped) - { - QueueDel(armor); - } - else - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-armor-on", - ("Entity", uid)), uid, PopupType.LargeCaution); - args.Handled = true; - } - } - } - - private void OnSpiderLegsActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistSpiderLegsActionEvent args) - { - _inventory.TryGetSlotEntity(uid, "outerClothing", out var outerClothing); - if (outerClothing != null) - { - if (!TryComp(outerClothing, out MetaDataComponent? metaData)) - return; - if (metaData.EntityPrototype == null) - return; - if (metaData.EntityPrototype.ID == component.ArmorSpawnId || metaData.EntityPrototype.ID == component.HeavyArmorSpawnId) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-spider-legs-blocked"), - uid, uid, PopupType.Large); - args.Handled = true; - return; - } - - if (_tagSystem.HasTag(outerClothing.Value, "FullBodyOuter")) - { - _inventory.TryUnequip(uid, "outerClothing", true, true); - args.Handled = true; - } - } - _inventory.TryGetSlotEntity(uid, "shoes", out var shoes); - if (shoes != null) - { - if (!TryComp(shoes, out MetaDataComponent? metaData)) - return; - if (metaData.EntityPrototype == null) - return; - - if (metaData.EntityPrototype.ID == component.SpiderLegsSpawnId) - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-spider-legs-off", - ("Entity", uid)), uid, PopupType.LargeCaution); - EntityManager.DeleteEntity(shoes.Value); - _movement.RefreshMovementSpeedModifiers(uid); - args.Handled = true; - return; - } - - _inventory.TryUnequip(uid, "shoes", true, true); - } - _inventory.TryUnequip(uid, "socks", true, true); - var legs = Spawn(component.SpiderLegsSpawnId, Transform(uid).Coordinates); - var equipped = _inventory.TryEquip(uid, legs, "shoes", true, true); - if (!equipped) - { - QueueDel(legs); - } - else - { - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-transform-spider-legs-on", - ("Entity", uid)), uid, PopupType.LargeCaution); - args.Handled = true; - } - } - - private void OnAdrenalinActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistAdrenalinActionEvent args) - { - if (!_solutionContainerSystem.TryGetInjectableSolution(uid, out var injectableSolution, out var _)) - return; - var transferSolution = new Solution(); - foreach (var solution in component.AdrenalinReagents) - { - transferSolution.AddReagent(solution.Reagent, solution.Quantity); - } - _solutionContainerSystem.TryAddSolution(injectableSolution.Value, transferSolution); - args.Handled = true; - } - - private void OnCreateFleshHeartActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistCreateFleshHeartActionEvent args) - { - var xform = Transform(uid); - var radius = 1.5f; - if (!TryComp(xform.GridUid, out var grid)) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-cant-spawn-flesh-heart-in-space", - ("Entity", uid)), uid, PopupType.Large); - return; - } - - var offsetValue = Vector2Helpers.Normalized(xform.LocalRotation.ToWorldVec()); - var targetCord = xform.Coordinates.Offset(offsetValue).SnapToGrid(EntityManager); - var tilerefs = Enumerable.ToArray(grid.GetLocalTilesIntersecting( - new Box2(targetCord.Position + new Vector2(-radius, -radius), targetCord.Position + new Vector2(radius, radius)))); - foreach (var tileref in tilerefs) - { - foreach (var entity in tileref.GetEntitiesInTile()) - { - PhysicsComponent? physics = null; // We use this to check if it's impassable - if (HasComp(entity) && entity != uid || // Is it a mob? - Resolve(entity, ref physics, false) && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0 || - HasComp(entity) && entity != uid) // Is construction? - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-cant-spawn-flesh-heart-here", - ("Entity", uid)), uid, PopupType.Large); - return; - } - } - } - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - EntityManager.SpawnEntity(component.FleshHeartId, targetCord); - args.Handled = true; - } - - private void OnThrowHugger(EntityUid uid, FleshCultistComponent component, FleshCultistThrowHuggerActionEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - var hugger = Spawn(component.HuggerMobSpawnId, Transform(uid).Coordinates); - var xform = Transform(uid); - var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); - var direction = mapCoords.Position - xform.MapPosition.Position; - - _throwing.TryThrow(hugger, direction, 7F, uid, 10F); - if (component.SoundThrowHugger != null) - { - _audioSystem.PlayPvs(component.SoundThrowHugger, uid, component.SoundThrowHugger.Params); - } - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-throw-hugger"), uid, uid, - PopupType.LargeCaution); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-throw-hugger-others", ("Entity", uid)), - uid, Filter.PvsExcept(uid), true, PopupType.LargeCaution); - } - -} diff --git a/Content.Server/_Sunrise/FleshCult/FleshCultistSystem.cs b/Content.Server/_Sunrise/FleshCult/FleshCultistSystem.cs deleted file mode 100644 index b61da05f63..0000000000 --- a/Content.Server/_Sunrise/FleshCult/FleshCultistSystem.cs +++ /dev/null @@ -1,808 +0,0 @@ -using System.Linq; -using Content.Server._Sunrise.FleshCult.GameRule; -using Content.Server.Actions; -using Content.Server.Atmos.Components; -using Content.Server.Body.Components; -using Content.Server.Body.Systems; -using Content.Server.Chat.Systems; -using Content.Server.Cuffs; -using Content.Server.Explosion.EntitySystems; -using Content.Server.Flash.Components; -using Content.Server.Fluids.EntitySystems; -using Content.Server.Forensics; -using Content.Server.Humanoid; -using Content.Server.Mind; -using Content.Server.Popups; -using Content.Server.Store.Systems; -using Content.Server.Sunrise.FleshCult; -using Content.Server.Temperature.Components; -using Content.Server.Traits.Assorted; -using Content.Server.Weapons.Ranged.Systems; -using Content.Shared._Sunrise.FleshCult; -using Content.Shared.Alert; -using Content.Shared.Body.Components; -using Content.Shared.Body.Part; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Cuffs.Components; -using Content.Shared.Damage; -using Content.Shared.DoAfter; -using Content.Shared.Electrocution; -using Content.Shared.FixedPoint; -using Content.Shared.Fluids.Components; -using Content.Shared.Hands.Components; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; -using Content.Shared.Interaction.Components; -using Content.Shared.Inventory; -using Content.Shared.Inventory.Events; -using Content.Shared.Jittering; -using Content.Shared.Mind.Components; -using Content.Shared.Mindshield.Components; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.Movement.Systems; -using Content.Shared.Nutrition.Components; -using Content.Shared.Popups; -using Content.Shared.Speech.EntitySystems; -using Content.Shared.Store.Components; -using Content.Shared.Stunnable; -using Content.Shared.Sunrise.CollectiveMind; -using Content.Shared.Tag; -using Content.Shared.Throwing; -using Robust.Server.Containers; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Collections; -using Robust.Shared.Containers; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; - -namespace Content.Server._Sunrise.FleshCult; - -public sealed partial class FleshCultistSystem : SharedFleshCultistSystem -{ - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly ActionsSystem _action = default!; - [Dependency] private readonly AlertsSystem _alerts = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly StoreSystem _store = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly PuddleSystem _puddleSystem = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly HumanoidAppearanceSystem _sharedHuApp = default!; - [Dependency] private readonly SharedAppearanceSystem _sharedAppearance = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly BodySystem _body = default!; - [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; - [Dependency] private readonly GunSystem _gunSystem = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly TagSystem _tagSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly CuffableSystem _cuffable = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedStunSystem _stunSystem = default!; - [Dependency] private readonly ChatSystem _chatSystem = default!; - [Dependency] private readonly FleshCultRuleSystem _fleshCultRule = default!; - [Dependency] private readonly SharedJitteringSystem _jittering = default!; - [Dependency] private readonly SharedStutteringSystem _stuttering = default!; - [Dependency] private readonly ExplosionSystem _explosionSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; - [Dependency] private readonly ContainerSystem _containerSystem = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnShop); - SubscribeLocalEvent(OnInsulatedImmunityMutation); - SubscribeLocalEvent(OnPressureImmunityMutation); - SubscribeLocalEvent(OnFlashImmunityMutation); - SubscribeLocalEvent(OnRespiratorImmunityMutation); - SubscribeLocalEvent(OnColdTempImmunityMutation); - SubscribeLocalEvent(OnDevourAction); - SubscribeLocalEvent(OnDevourDoAfter); - SubscribeLocalEvent(OnBeingEquippedAttempt); - SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnAbsormBloodPoolActionEvent); - - SubscribeLocalEvent(OnPendingMapInit); - - InitializeAbilities(); - } - - private void OnPendingMapInit(EntityUid uid, PendingFleshCultistComponent component, MapInitEvent args) - { - component.NextParalyze = _timing.CurTime + TimeSpan.FromSeconds(1f); - component.NextScream = _timing.CurTime + TimeSpan.FromSeconds(1f); - } - - private void OnMobStateChanged(EntityUid uid, FleshCultistComponent component, MobStateChangedEvent args) - { - switch (args.NewMobState) - { - case MobState.Critical: - { - EnsureComp(uid); - var hands = _handsSystem.EnumerateHands(uid); - var enumerateHands = hands as Hand[] ?? hands.ToArray(); - foreach (var enumerateHand in enumerateHands) - { - if (enumerateHand.Container == null) - continue; - foreach (var containerContainedEntity in enumerateHand.Container.ContainedEntities) - { - if (HasComp<_Sunrise.FleshCult.FleshHandModComponent>(containerContainedEntity)) - continue; - QueueDel(containerContainedEntity); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - } - } - - break; - } - case MobState.Dead: - { - _inventory.TryGetSlotEntity(uid, "shoes", out var shoes); - if (shoes != null) - { - if (TryComp(shoes, out MetaDataComponent? metaData)) - { - if (metaData.EntityPrototype != null) - { - if (metaData.EntityPrototype.ID == component.SpiderLegsSpawnId) - { - EntityManager.DeleteEntity(shoes.Value); - _movement.RefreshMovementSpeedModifiers(uid); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - } - } - } - } - - _inventory.TryGetSlotEntity(uid, "outerClothing", out var outerClothing); - if (outerClothing != null) - { - if (TryComp(outerClothing, out MetaDataComponent? metaData)) - { - if (metaData.EntityPrototype != null) - { - if (metaData.EntityPrototype.ID == component.ArmorSpawnId || - metaData.EntityPrototype.ID == component.HeavyArmorSpawnId) - { - EntityManager.DeleteEntity(outerClothing.Value); - _movement.RefreshMovementSpeedModifiers(uid); - _audioSystem.PlayPvs(component.SoundMutation, uid, component.SoundMutation.Params); - } - } - } - } - - ParasiteComesOut(uid, component); - break; - } - } - } - - private void OnBeingEquippedAttempt(EntityUid uid, FleshCultistComponent component, IsEquippingAttemptEvent args) - { - if (args.Slot is not ("socks" or "outerClothing")) - return; - _inventory.TryGetSlotEntity(uid, "shoes", out var shoes); - if (shoes == null) - return; - if (!TryComp(shoes, out MetaDataComponent? metaData)) - return; - if (metaData.EntityPrototype == null) - return; - if (metaData.EntityPrototype.ID != component.SpiderLegsSpawnId) - return; - if (args.Slot is "outerClothing" && !_tagSystem.HasTag(args.Equipment, "FullBodyOuter")) - return; - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-equiped-outer-clothing-blocked", - ("Entity", uid)), uid, PopupType.Large); - args.Cancel(); - - } - - private void OnStartup(EntityUid uid, FleshCultistComponent component, ComponentStartup args) - { - ChangeParasiteHunger(uid, 0, component); - - _action.AddAction(uid, "FleshCultistShop"); - _action.AddAction(uid, "FleshCultistDevour"); - _action.AddAction(uid, "FleshCultistAbsorbBloodPool"); - - var storeComp = EnsureComp(uid); - - var collectiveMindComponent = EnsureComp(uid); - if (!collectiveMindComponent.Minds.Contains("FleshCult")) - collectiveMindComponent.Minds.Add("FleshCult"); - - storeComp.Categories.Add("FleshCultistPassiveSkills"); - storeComp.Categories.Add("FleshCultistActiveSkills"); - storeComp.Categories.Add("FleshCultistWeapon"); - storeComp.Categories.Add("FleshCultistArmor"); - storeComp.CurrencyWhitelist.Add("StolenMutationPoint"); - storeComp.BuySuccessSound = component.BuySuccesSound; - storeComp.RefundAllowed = false; - - EnsureComp(uid); - - if (HasComp(uid)) - RemComp(uid); - - if (HasComp(uid)) - RemComp(uid); - - _tagSystem.AddTag(uid, "Flesh"); - - if (TryComp(uid, out var appearance)) - { - appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.RLeg); - appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.LLeg); - appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.RFoot); - appearance.HideLayersOnEquip.Add(HumanoidVisualLayers.LFoot); - Dirty(uid, appearance); - } - } - - private void OnInsulatedImmunityMutation(EntityUid uid, FleshCultistComponent component, - FleshCultistInsulatedImmunityMutationEvent args) - { - EnsureComp(uid); - } - - - private void OnPressureImmunityMutation(EntityUid uid, FleshCultistComponent component, - FleshCultistPressureImmunityMutationEvent args) - { - EnsureComp(uid); - } - - private void OnFlashImmunityMutation(EntityUid uid, FleshCultistComponent component, - FleshCultistFlashImmunityMutationEvent args) - { - EnsureComp(uid); - } - - private void OnRespiratorImmunityMutation(EntityUid uid, FleshCultistComponent component, - FleshCultistRespiratorImmunityMutationEvent args) - { - EnsureComp(uid); - } - - private void OnColdTempImmunityMutation(EntityUid uid, FleshCultistComponent component, - FleshCultistColdTempImmunityMutationEvent args) - { - if (TryComp(uid, out var tempComponent)) - { - tempComponent.ColdDamageThreshold = 0; - } - } - - private void OnShop(EntityUid uid, FleshCultistComponent component, FleshCultistShopActionEvent args) - { - if (!TryComp(uid, out var store)) - return; - _store.ToggleUi(uid, uid, store); - } - - private bool ChangeParasiteHunger(EntityUid uid, FixedPoint2 amount, FleshCultistComponent? component = null) - { - if (!Resolve(uid, ref component)) - return false; - - component.Hunger += amount; - - if (TryComp(uid, out var store)) - _store.UpdateUserInterface(uid, uid, store); - - _alerts.ShowAlert(uid, component.MutationPointAlert, (short) Math.Clamp(Math.Round(component.Hunger.Float() / 10f), 0, 16)); - - return true; - } - - private void OnDevourAction(EntityUid uid, FleshCultistComponent component, FleshCultistDevourActionEvent args) - { - if (args.Handled) - return; - - var target = args.Target; - - if (!TryComp(target, out var targetState)) - return; - if (!TryComp(target, out var bloodstream)) - return; - var hasAppearance = false; - { - switch (targetState.CurrentState) - { - case MobState.Dead: - if (EntityManager.TryGetComponent(target, out HumanoidAppearanceComponent? humanoidAppearance)) - { - if (!component.SpeciesWhitelist.Contains(humanoidAppearance.Species)) - { - _popupSystem.PopupEntity( - Loc.GetString("flesh-cultist-devout-target-not-have-flesh"), - uid, uid); - return; - } - - if (TryComp(target, out var fixturesComponent)) - { - if (fixturesComponent.Fixtures["fix1"].Density <= 60) - { - _popupSystem.PopupEntity( - Loc.GetString("flesh-cultist-devout-target-invalid"), - uid, uid); - return; - } - } - - hasAppearance = true; - } - else - { - if (bloodstream.BloodReagent != "Blood") - { - _popupSystem.PopupEntity( - Loc.GetString("flesh-cultist-devout-target-not-have-flesh"), - uid, uid); - return; - } - if (bloodstream.BloodMaxVolume < 30) - { - _popupSystem.PopupEntity( - Loc.GetString("flesh-cultist-devout-target-invalid"), - uid, uid); - return; - } - } - var saturation = MatchSaturation(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); - if (component.Hunger + saturation >= component.MaxHunger) - { - _popupSystem.PopupEntity( - Loc.GetString("flesh-cultist-devout-not-hungry"), - uid, uid); - return; - } - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager ,uid, component.DevourTime, - new FleshCultistDevourDoAfterEvent(), uid, target: target, used: uid) - { - BreakOnMove = true, - }); - args.Handled = true; - break; - - case MobState.Invalid: - case MobState.Critical: - case MobState.Alive: - default: - _popupSystem.PopupEntity( - Loc.GetString("flesh-cultist-devout-target-alive"), - uid, uid); - break; - } - } - } - - private void OnDevourDoAfter(EntityUid uid, FleshCultistComponent component, FleshCultistDevourDoAfterEvent args) - { - if (args.Handled || args.Cancelled) - return; - - if (args.Args.Target == null) - return; - - if (!TryComp(args.Args.Target.Value, out var bloodstream)) - return; - - var hasAppearance = false; - - var xform = Transform(args.Args.Target.Value); - var coordinates = xform.Coordinates; - _audioSystem.PlayPvs(component.DevourSound, coordinates, AudioParams.Default.WithVariation(0.025f).WithMaxDistance(5f)); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-devour-target", - ("Entity", uid), ("Target", args.Args.Target)), uid); - - if (bloodstream.BloodSolution != null) - { - _bloodstreamSystem.SpillAllSolutions(args.Args.Target.Value, bloodstream); - } - - if (TryComp(args.Args.Target, out var HuAppComponent)) - { - if (TryComp(args.Args.Target.Value, out ContainerManagerComponent? container)) - { - foreach (var cont in container.GetAllContainers().ToArray()) - { - foreach (var ent in cont.ContainedEntities.ToArray()) - { - if (HasComp(ent)) - { - continue; - } - _containerSystem.Remove(ent, cont, force: true); - Transform(ent).Coordinates = coordinates; - } - } - } - - if (TryComp(args.Args.Target, out var bodyComponent)) - { - var parts = _body.GetBodyChildren(args.Args.Target, bodyComponent).ToArray(); - - foreach (var part in parts) - { - if (part.Component.PartType == BodyPartType.Head) - continue; - - if (part.Component.PartType == BodyPartType.Torso) - { - foreach (var organ in _body.GetPartOrgans(part.Id, part.Component)) - { - //_body.RemoveOrgan(organ.Id); - QueueDel(organ.Id); - } - } - else - { - QueueDel(part.Id); - } - } - } - - var skeletonSprites = _proto.Index("MobSkeletonSprites"); - foreach (var (key, id) in skeletonSprites.Sprites) - { - if (key != HumanoidVisualLayers.Head) - { - _sharedHuApp.SetBaseLayerId(args.Args.Target.Value, key, id, humanoid: HuAppComponent); - } - } - - if (TryComp(args.Args.Target, out var fixturesComponent)) - { - _physics.SetDensity(args.Args.Target.Value, "fix1", fixturesComponent.Fixtures["fix1"], 50); - } - - if (TryComp(args.Args.Target, out var appComponent)) - { - _sharedAppearance.SetData(args.Args.Target.Value, DamageVisualizerKeys.Disabled, true, appComponent); - } - - hasAppearance = true; - } - - var saturation = MatchSaturation(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); - var evolutionPoint = MatchEvolutionPoint(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); - var healPoint = MatchHealPoint(bloodstream.BloodMaxVolume.Value / 100, hasAppearance); - - RemComp(args.Args.Target.Value); - - EnsureComp(args.Args.Target.Value); - - if (!hasAppearance) - { - QueueDel(args.Args.Target.Value); - } - - if (_solutionContainerSystem.TryGetInjectableSolution(uid, out var injectableSolution, out _)) - { - var transferSolution = new Solution(); - foreach (var solution in component.HealDevourReagents) - { - transferSolution.AddReagent(solution.Reagent, solution.Quantity * healPoint); - } - _solutionContainerSystem.TryAddSolution(injectableSolution.Value, transferSolution); - } - - component.Hunger += saturation; - _store.TryAddCurrency(new Dictionary - { {component.StolenCurrencyPrototype, evolutionPoint} }, uid); - } - - private int MatchSaturation(int bloodVolume, bool hasAppearance) - { - if (hasAppearance) - { - return 100; - } - return bloodVolume switch - { - >= 300 => 80, - >= 150 => 60, - >= 100 => 40, - _ => 20 - }; - } - - private int MatchEvolutionPoint(int bloodVolume, bool hasAppearance) - { - if (hasAppearance) - { - return 20; - } - return bloodVolume switch - { - >= 300 => 15, - >= 150 => 10, - >= 100 => 5, - _ => 0 - }; - } - - private float MatchHealPoint(int bloodVolume, bool hasAppearance) - { - if (hasAppearance) - { - return 1; - } - return bloodVolume switch - { - >= 300 => 0.8f, - >= 150 => 0.6f, - >= 100 => 0.4f, - _ => 0.2f - }; - } - - private bool ParasiteComesOut(EntityUid uid, FleshCultistComponent? component = null) - { - if (!Resolve(uid, ref component)) - return false; - - var xform = Transform(uid); - var coordinates = xform.Coordinates; - - var abommob = Spawn(component.FleshMutationMobId, _transformSystem.GetMapCoordinates(uid)); - - if (_mindSystem.TryGetMind(uid, out var mindId, out var mind)) - { - _mindSystem.TransferTo(mindId, abommob, ghostCheckOverride: true); - } - - _popupSystem.PopupEntity(Loc.GetString("flesh-pudge-transform-user", ("EntityTransform", uid)), - uid, uid, PopupType.LargeCaution); - _popupSystem.PopupEntity(Loc.GetString("flesh-pudge-transform-others", - ("Entity", uid), ("EntityTransform", abommob)), abommob, Filter.PvsExcept(abommob), - true, PopupType.LargeCaution); - - _audioSystem.PlayPvs(component.SoundMutation, coordinates, AudioParams.Default.WithVariation(0.025f)); - - if (TryComp(uid, out ContainerManagerComponent? container)) - { - foreach (var cont in container.GetAllContainers().ToArray()) - { - foreach (var ent in cont.ContainedEntities.ToArray()) - { - if (HasComp(ent)) - continue; - if (HasComp(ent)) - continue; - _containerSystem.Remove(ent, cont, force: true); - Transform(ent).Coordinates = coordinates; - } - } - } - - if (TryComp(uid, out var bloodstream)) - { - var tempSol = new Solution() { MaxVolume = 5 }; - - if (bloodstream.BloodSolution == null) - return false; - - tempSol.AddSolution(bloodstream.BloodSolution.Value.Comp.Solution, _proto); - - if (_puddleSystem.TrySpillAt(uid, tempSol.SplitSolution(50), out var puddleUid)) - { - if (TryComp(uid, out var dna)) - { - var comp = EnsureComp(puddleUid); - comp.DNAs.Add(dna.DNA); - } - } - } - - QueueDel(uid); - return true; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - var curTime = _timing.CurTime; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) - { - if (comp.CurrentStage == PendingFleshCultistStage.Final) - continue; - - comp.Accumulator += frameTime; - - var stageTimer = comp.CurrentStage switch - { - PendingFleshCultistStage.First => comp.FirstStageTimer, - PendingFleshCultistStage.Second => comp.SecondStageTimer, - _ => 0f - }; - - if (comp.Accumulator >= stageTimer) - { - comp.Accumulator = 0f; - comp.CurrentStage = GetNextStage(comp.CurrentStage); - } - - switch (comp.CurrentStage) - { - case PendingFleshCultistStage.First: - if (comp.NextScream <= curTime) - { - comp.NextScream = curTime + TimeSpan.FromSeconds(comp.ScreamInterval); - _chatSystem.TryEmoteWithChat(uid, "Scream"); - } - if (comp.NextStutter <= curTime) - { - comp.NextStutter = curTime + TimeSpan.FromSeconds(comp.ScreamInterval); - _stuttering.DoStutter(uid, TimeSpan.FromSeconds(comp.StutterTime), true); - } - break; - case PendingFleshCultistStage.Second: - if (comp.NextParalyze <= curTime) - { - comp.NextParalyze = curTime + TimeSpan.FromSeconds(comp.ParalyzeInterval); - _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(comp.ParalyzeTime), true); - } - if (comp.NextJitter <= curTime) - { - comp.NextJitter = curTime + TimeSpan.FromSeconds(comp.JitterInterval); - _jittering.DoJitter(uid, TimeSpan.FromSeconds(comp.JitterTime), true); - } - break; - case PendingFleshCultistStage.Third: - { - if (!TryComp(uid, out var targetMindComp)) - return; - - if (HasComp(uid)) - { - _popupSystem.PopupEntity("Активация самоуничтожения импланта защиты разума", uid, PopupType.LargeCaution); - _body.GibBody(uid, true); - _explosionSystem.QueueExplosion(uid, "Default", 50, 5, 30, canCreateVacuum: false); - return; - } - - var fleshCultRule = _fleshCultRule.StartGameRule(); - _fleshCultRule.MakeCultist(uid, 0, fleshCultRule); - - comp.CurrentStage = PendingFleshCultistStage.Final; - break; - } - } - } - - foreach (var rev in EntityQuery()) - { - rev.Accumulator += frameTime; - - if (rev.Accumulator <= 1) - continue; - rev.Accumulator -= 1; - - if (rev.Hunger <= 40) - { - rev.AccumulatorStarveNotify += 1; - if (rev.AccumulatorStarveNotify > 30) - { - rev.AccumulatorStarveNotify = 0; - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-hungry"), - rev.Owner, rev.Owner, PopupType.Large); - } - } - - if (rev.Hunger < 0) - { - ParasiteComesOut(rev.Owner, rev); - } - - ChangeParasiteHunger(rev.Owner, rev.HungerСonsumption, rev); - } - } - - private PendingFleshCultistStage GetNextStage(PendingFleshCultistStage currentStage) - { - return currentStage switch - { - PendingFleshCultistStage.First => PendingFleshCultistStage.Second, - PendingFleshCultistStage.Second => PendingFleshCultistStage.Third, - PendingFleshCultistStage.Third => PendingFleshCultistStage.Final, - _ => currentStage, - }; - } - - private void OnAbsormBloodPoolActionEvent(EntityUid uid, FleshCultistComponent component, - FleshCultistAbsorbBloodPoolActionEvent args) - { - if (args.Handled) - return; - - var xform = Transform(uid); - var puddles = new ValueList<(EntityUid Entity, string Solution)>(); - puddles.Clear(); - foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, 1f)) - { - if (TryComp(entity, out var puddle)) - { - puddles.Add((entity, puddle.SolutionName)); - } - } - - if (puddles.Count == 0) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-not-find-puddles"), - uid, uid, PopupType.Large); - return; - } - - var absorbBlood = new Solution(); - foreach (var (puddle, solution) in puddles) - { - if (!_solutionContainerSystem.TryGetSolution(puddle, solution, out var puddleSolution)) - { - continue; - } - foreach (var puddleSolutionContent in puddleSolution.Value.Comp.Solution.ToList()) - { - if (!component.BloodWhitelist.Contains(puddleSolutionContent.Reagent.Prototype)) - continue; - - var blood = puddleSolution.Value.Comp.Solution.SplitSolutionWithOnly( - puddleSolutionContent.Quantity, puddleSolutionContent.Reagent.Prototype); - - absorbBlood.AddSolution(blood, _proto); - } - - var ev = new SolutionContainerChangedEvent(puddleSolution.Value.Comp.Solution, solution); - RaiseLocalEvent(puddle, ref ev); - } - - if (absorbBlood.Volume == 0) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-cant-absorb-puddle"), - uid, uid, PopupType.Large); - return; - } - - _audioSystem.PlayPvs(component.BloodAbsorbSound, uid, component.BloodAbsorbSound.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-absorb-puddle", ("Entity", uid)), - uid, uid, PopupType.Large); - - var transferSolution = new Solution(); - foreach (var solution in component.HealBloodAbsorbReagents) - { - transferSolution.AddReagent(solution.Reagent, solution.Quantity * (absorbBlood.Volume / 10)); - } - - if (_solutionContainerSystem.TryGetInjectableSolution(uid, out var injectableSolution, out var _)) - { - _solutionContainerSystem.TryAddSolution(injectableSolution.Value, transferSolution); - } - absorbBlood.RemoveAllSolution(); - args.Handled = true; - } -} diff --git a/Content.Server/_Sunrise/FleshCult/FleshGrowth/SpreaderFleshComponent.cs b/Content.Server/_Sunrise/FleshCult/FleshGrowth/SpreaderFleshComponent.cs index 08bfad19c0..595f6a1829 100644 --- a/Content.Server/_Sunrise/FleshCult/FleshGrowth/SpreaderFleshComponent.cs +++ b/Content.Server/_Sunrise/FleshCult/FleshGrowth/SpreaderFleshComponent.cs @@ -1,6 +1,6 @@ namespace Content.Server._Sunrise.FleshCult.FleshGrowth; -[RegisterComponent, Access(typeof(SpreaderFleshSystem), typeof(FleshHeartSystem))] +[RegisterComponent, Access(typeof(SpreaderFleshSystem), typeof(FleshCultSystem))] public sealed partial class SpreaderFleshComponent : Component { [DataField("chance", required: true)] diff --git a/Content.Server/_Sunrise/FleshCult/FleshHeartSystem.cs b/Content.Server/_Sunrise/FleshCult/FleshHeartSystem.cs deleted file mode 100644 index bc7f64d12f..0000000000 --- a/Content.Server/_Sunrise/FleshCult/FleshHeartSystem.cs +++ /dev/null @@ -1,466 +0,0 @@ -using System.Linq; -using System.Numerics; -using Content.Server._Sunrise.FleshCult.FleshGrowth; -using Content.Server._Sunrise.FleshCult.GameRule; -using Content.Server.Body.Systems; -using Content.Server.Chat.Systems; -using Content.Server.Humanoid; -using Content.Server.Popups; -using Content.Server.RoundEnd; -using Content.Server.Station.Systems; -using Content.Server.Traits.Assorted; -using Content.Shared._Sunrise.FleshCult; -using Content.Shared.Body.Components; -using Content.Shared.Body.Part; -using Content.Shared.Damage; -using Content.Shared.Destructible; -using Content.Shared.DoAfter; -using Content.Shared.DragDrop; -using Content.Shared.Flesh; -using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; -using Content.Shared.Mind.Components; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.Physics; -using Content.Shared.Popups; -using Content.Shared.Random.Helpers; -using Content.Shared.Tag; -using Robust.Server.Containers; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Containers; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server._Sunrise.FleshCult -{ - public sealed class FleshHeartSystem : EntitySystem - { - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly ContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly IMapManager _map = default!; - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly TagSystem _tagSystem = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly BodySystem _body = default!; - [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; - [Dependency] private readonly HumanoidAppearanceSystem _sharedHuApp = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly SharedAppearanceSystem _sharedAppearance = default!; - [Dependency] private readonly StationSystem _stationSystem = default!; - [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedMapSystem _mapSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnDestruction); - SubscribeLocalEvent(HandleDragDropOn); - SubscribeLocalEvent(OnDragFinished); - SubscribeLocalEvent(OnComponentInit); - } - - private void OnShutdown(EntityUid uid, FleshHeartComponent component, ComponentShutdown args) - { - _audioSystem.Stop(component.AmbientAudioStream); - } - - private void OnDestruction(EntityUid uid, FleshHeartComponent component, DestructionEventArgs args) - { - if (component.State != HeartStates.Base) - { - _audioSystem.Stop(component.AmbientAudioStream); - //var stationUid = _stationSystem.GetOwningStation(uid); - //if (stationUid != null) - //{ - // _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnDeactivate, true, - // true, true); - //} - var xform = Transform(uid); - var coordinates = xform.Coordinates; - foreach (var ent in component.BodyContainer.ContainedEntities.ToArray()) - { - _containerSystem.Remove(ent, component.BodyContainer, force: true); - Transform(ent).Coordinates = coordinates; - } - var fleshTilesQuery = EntityQueryEnumerator(); - while (fleshTilesQuery.MoveNext(out var ent, out var comp)) - { - if (comp.Source != uid) - continue; - if (!TryComp(ent, out var tagComponent)) - continue; - if (_tagSystem.HasAllTags(tagComponent, "Wall", "Flesh")) - _damageableSystem.TryChangeDamage(ent, component.DamageMobsIfHeartDestruct); - else - QueueDel(ent); - - } - var fleshWalls = new List(); - var fleshWallsQuery = EntityQueryEnumerator(); - while (fleshWallsQuery.MoveNext(out var ent, out var comp)) - { - if (!TryComp(ent, out var tagComponent)) - continue; - var isFleshWall = _tagSystem.HasAllTags(tagComponent, "Wall", "Flesh"); - if (isFleshWall) - { - fleshWalls.Add(ent); - } - } - foreach (var mob in component.EdgeMobs.ToArray()) - { - _damageableSystem.TryChangeDamage(mob, component.DamageMobsIfHeartDestruct); - } - RaiseLocalEvent(new FleshHeartDestructionEvent() - { - FleshHeardUid = uid, - OwningStation = xform.GridUid - }); - } - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - var fleshHeartQuery = EntityQueryEnumerator(); - while (fleshHeartQuery.MoveNext(out var ent, out var comp, out var xform)) - { - var fleshCultRule = EntityQuery().FirstOrDefault(); - if (fleshCultRule == null) - { - continue; - } - - var owningStation = _stationSystem.GetOwningStation(ent, xform); - - if (owningStation != fleshCultRule.TargetStation) - continue; - - switch (comp.State) - { - case HeartStates.Base: - { - comp.Accumulator += frameTime; - - if (comp.Accumulator <= 1) - continue; - - comp.Accumulator -= 1; - if (comp.BodyContainer.ContainedEntities.Count >= comp.BodyToFinalStage) - { - comp.SpawnMobsAccumulator = 500; - comp.State = HeartStates.Active; - RaiseLocalEvent(new FleshHeartActivateEvent() - { - FleshHeardUid = ent, - OwningStation = xform.GridUid - }); - _chat.DispatchGlobalAnnouncement( - Loc.GetString("flesh-heart-activate-warning"), - colorOverride: Color.Red); - // _audioSystem.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true); - var stationUid = _stationSystem.GetOwningStation(ent); - //if (stationUid != null) - //{ - // _alertLevel.SetLevel(stationUid.Value, comp.AlertLevelOnActivate, false, - // true, true, true); - //} - //_audioSystem.PlayGlobal( - // "/Audio/_Sunrise/FleshCult/flesh_heart_activate.ogg", Filter.Broadcast(), true, - // AudioParams.Default); - SpawnFleshFloorOnOpenTiles(ent, comp, Transform(ent), 1); - _roundEndSystem.CancelRoundEndCountdown(stationUid); - _audioSystem.PlayPvs(comp.TransformSound, ent, comp.TransformSound.Params); - comp.AmbientAudioStream = _audioSystem.PlayGlobal( - "/Audio/_Sunrise/FleshCult/flesh_heart.ogg", Filter.Broadcast(), true, - AudioParams.Default.WithLoop(true).WithVolume(-3f))!.Value.Entity; - _appearance.SetData(ent, FleshHeartVisuals.State, FleshHeartStatus.Active); - } - break; - } - case HeartStates.Active: - { - comp.SpawnMobsAccumulator += frameTime; - comp.SpawnObjectsAccumulator += frameTime; - comp.FinalStageAccumulator += frameTime; - if (comp.SpawnMobsAccumulator >= comp.SpawnMobsFrequency) - { - comp.SpawnMobsAccumulator = 0; - SpawnMonstersOnOpenTiles(comp, xform, comp.SpawnMobsAmount, comp.SpawnMobsRadius); - } - - if (comp.SpawnObjectsAccumulator >= comp.SpawnObjectsFrequency) - { - comp.SpawnObjectsAccumulator = 0; - // SpawnObjectsOnOpenTiles(comp, xform, comp.SpawnObjectsAmount, comp.SpawnObjectsRadius); - } - - if (comp.FinalStageAccumulator >= comp.TimeLiveFinalHeartToWin) - { - comp.State = HeartStates.Disable; - RaiseLocalEvent(new FleshHeartFinalEvent() - { - FleshHeardUid = ent, - OwningStation = owningStation, - }); - } - break; - } - case HeartStates.Disable: - { - break; - } - default: - throw new ArgumentOutOfRangeException(); - } - } - } - - #region Interaction - - private void HandleDragDropOn(EntityUid uid, FleshHeartComponent component, ref DragDropTargetEvent args) - { - - if (!CanAbsorb(uid, args.Dragged, component)) - { - _popup.PopupEntity(Loc.GetString("flesh-heart-cant-absorb-targer"), - args.User, PopupType.Large); - return; - } - - if (!TryComp(args.Dragged, out var fixturesComponent)) - { - _popup.PopupEntity(Loc.GetString("flesh-heart-cant-absorb-targer"), - args.User, PopupType.Large); - return; - } - - if (fixturesComponent.Fixtures["fix1"].Density <= 60) - { - _popup.PopupEntity( - Loc.GetString("flesh-heart-cant-absorb-targer"), - uid, PopupType.Large); - return; - } - - var doAfterArgs = new DoAfterArgs(EntityManager, args.User, component.EntryDelay, new FleshHeartDragFinished(), uid, target: args.Dragged, used: uid) - { - BreakOnDamage = true, - BreakOnMove = true, - NeedHand = false, - }; - _doAfterSystem.TryStartDoAfter(doAfterArgs); - args.Handled = true; - } - - protected void OnComponentInit(EntityUid uid, FleshHeartComponent cryoPodComponent, ComponentInit args) - { - cryoPodComponent.BodyContainer = _containerSystem.EnsureContainer(uid, "bodyContainer"); - } - - private void OnDragFinished(EntityUid uid, FleshHeartComponent component, FleshHeartDragFinished args) - { - if (args.Cancelled || args.Handled || args.Args.Target == null) - return; - - if (!TryComp(args.Args.Target.Value, out var fixturesComponent)) - { - _popup.PopupEntity(Loc.GetString("flesh-heart-cant-absorb-targer"), - args.User, PopupType.Large); - return; - } - - var xform = Transform(args.Args.Target.Value); - - if (TryComp(args.Args.Target.Value, out ContainerManagerComponent? container)) - { - foreach (var cont in container.GetAllContainers().ToArray()) - { - foreach (var ent in cont.ContainedEntities.ToArray()) - { - { - if (HasComp(ent)) - { - continue; - } - _containerSystem.Remove(ent, cont, force: true); - Transform(ent).Coordinates = xform.Coordinates; - } - } - } - } - - if (TryComp(args.Args.Target.Value, out var HuAppComponent)) - { - if (TryComp(args.Args.Target.Value, out var bodyComponent)) - { - var parts = _body.GetBodyChildren(args.Args.Target.Value, bodyComponent).ToArray(); - - foreach (var part in parts) - { - if (part.Component.PartType == BodyPartType.Head) - continue; - - if (part.Component.PartType == BodyPartType.Torso) - { - foreach (var organ in _body.GetPartOrgans(part.Id, part.Component)) - { - _body.RemoveOrgan(organ.Id); - } - } - else - { - QueueDel(part.Id); - } - } - } - - _bloodstreamSystem.TryModifyBloodLevel(args.Args.Target.Value, -300); - - var skeletonSprites = _proto.Index("MobSkeletonSprites"); - foreach (var (key, id) in skeletonSprites.Sprites) - { - if (key != HumanoidVisualLayers.Head) - { - _sharedHuApp.SetBaseLayerId(args.Args.Target.Value, key, id, humanoid: HuAppComponent); - } - } - - _physics.SetDensity(args.Args.Target.Value, "fix1", fixturesComponent.Fixtures["fix1"], 50); - - if (TryComp(args.Args.Target.Value, out var appComponent)) - { - _sharedAppearance.SetData(args.Args.Target.Value, DamageVisualizerKeys.Disabled, true, appComponent); - _damageableSystem.TryChangeDamage(args.Args.Target.Value, - new DamageSpecifier() { DamageDict = { { "Slash", 100 } } }); - } - - EnsureComp(args.Args.Target.Value); - - _containerSystem.Insert(args.Args.Target.Value, component.BodyContainer, force: true); - _audioSystem.PlayPvs(component.TransformSound, uid, component.TransformSound.Params); - } - - args.Handled = true; - } - - #endregion - - private bool CanAbsorb(EntityUid uid, EntityUid dragged, FleshHeartComponent component) - { - if (!TryComp(dragged, out var stateComponent)) - return false; - - if (stateComponent.CurrentState != MobState.Dead) - return false; - - if (!Transform(uid).Anchored) - return false; - - if (!TryComp(dragged, out var humanoidAppearance)) - return false; - - if (!(component.SpeciesWhitelist.Contains(humanoidAppearance.Species))) - return false; - - return !TryComp(dragged, out var mindComp) || true; - } - - private void SpawnFleshFloorOnOpenTiles(EntityUid fleshHeart, FleshHeartComponent component, TransformComponent xform, float radius) - { - if (!TryComp(xform.GridUid, out var grid)) - return; - - var localpos = xform.Coordinates.Position; - var tilerefs = grid.GetLocalTilesIntersecting( - new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius))).ToArray(); - foreach (var tileref in tilerefs) - { - var canSpawnFloor = true; - foreach (var ent in grid.GetAnchoredEntities(tileref.GridIndices).ToList()) - { - if (_tagSystem.HasAnyTag(ent, "Wall", "Window", "Flesh")) - canSpawnFloor = false; - } - if (canSpawnFloor) - { - var location = _mapSystem.ToCenterCoordinates(tileref, grid); - var fleshTile = EntityManager.SpawnEntity(component.FleshTileId, location); - var spreaderFleshComponent = EnsureComp(fleshTile); - spreaderFleshComponent.Source = fleshHeart; - } - } - } - - private void SpawnMonstersOnOpenTiles(FleshHeartComponent component, TransformComponent xform, int amount, float radius) - { - if (!TryComp(xform.GridUid, out var grid)) - return; - - var localpos = xform.Coordinates.Position; - var tilerefs = grid.GetLocalTilesIntersecting( - new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius))).ToArray(); - _random.Shuffle(tilerefs); - var physQuery = GetEntityQuery(); - var amountCounter = 0; - foreach (var tileref in tilerefs) - { - var valid = true; - foreach (var ent in grid.GetAnchoredEntities(tileref.GridIndices)) - { - if (!physQuery.TryGetComponent(ent, out var body)) - continue; - if (body.BodyType != BodyType.Static || - !body.Hard || - (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0) - continue; - valid = false; - break; - } - if (!valid) - continue; - amountCounter++; - - var randomMob = _random.Pick(component.Spawns); - var location = _mapSystem.ToCenterCoordinates(tileref, grid); - var mob = Spawn(randomMob, location); - component.EdgeMobs.Add(mob); - if (amountCounter >= amount) - return; - } - } - - public sealed class FleshHeartFinalEvent : EntityEventArgs - { - public EntityUid FleshHeardUid; - public EntityUid? OwningStation; - } - - public sealed class FleshHeartActivateEvent : EntityEventArgs - { - public EntityUid FleshHeardUid; - public EntityUid? OwningStation; - } - - public sealed class FleshHeartDestructionEvent : EntityEventArgs - { - public EntityUid FleshHeardUid; - public EntityUid? OwningStation; - } - } -} diff --git a/Content.Server/_Sunrise/FleshCult/FleshHuggerSystem.cs b/Content.Server/_Sunrise/FleshCult/FleshHuggerSystem.cs deleted file mode 100644 index 695aa47007..0000000000 --- a/Content.Server/_Sunrise/FleshCult/FleshHuggerSystem.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System.Linq; -using Content.Server.Actions; -using Content.Server.Flesh; -using Content.Server.Nutrition.Components; -using Content.Server.Popups; -using Content.Shared._Sunrise.FleshCult; -using Content.Shared.CombatMode; -using Content.Shared.CombatMode.Pacification; -using Content.Shared.Damage; -using Content.Shared.Eye.Blinding.Components; -using Content.Shared.Hands; -using Content.Shared.Humanoid; -using Content.Shared.IdentityManagement.Components; -using Content.Shared.Inventory; -using Content.Shared.Inventory.Events; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.Popups; -using Content.Shared.Stunnable; -using Content.Shared.Throwing; -using Content.Shared.Weapons.Melee.Events; -using Robust.Server.GameObjects; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using FleshHuggerComponent = Content.Shared._Sunrise.FleshCult.FleshHuggerComponent; - -namespace Content.Server._Sunrise.FleshCult -{ - public sealed class FleshHuggerSystem : SharedFleshHuggerSystem - { - [Dependency] private SharedStunSystem _stunSystem = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly ActionsSystem _action = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; - - - public override void Initialize() - { - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnMeleeHit); - SubscribeLocalEvent(OnWormDoHit); - SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnGotUnequipped); - SubscribeLocalEvent(OnGotEquippedHand); - SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnJump); - SubscribeLocalEvent(OnGetOff); - } - - private void OnMapInit(EntityUid uid, FleshHuggerComponent component, MapInitEvent args) - { - _action.AddAction(uid, component.ActionFleshHuggerJumpId); - _action.AddAction(uid, component.ActionFleshHuggerGetOffId); - } - - private void OnWormDoHit(EntityUid uid, FleshHuggerComponent component, ThrowDoHitEvent args) - { - if (component.IsDeath) - return; - if (HasComp(args.Target)) - return; - if (!HasComp(args.Target)) - return; - if (TryComp(args.Target, out MobStateComponent? mobState)) - { - if (mobState.CurrentState is not MobState.Alive) - { - return; - } - } - _inventory.TryGetSlotEntity(args.Target, "head", out var headItem); - if (HasComp(headItem)) - return; - - _inventory.TryGetSlotEntity(args.Target, "mask", out var maskItem); - if (HasComp(maskItem)) - return; - - _inventory.TryUnequip(args.Target, "head", true); - _inventory.TryUnequip(args.Target, "eyes", true); - _inventory.TryUnequip(args.Target, "mask", true); - - var equipped = _inventory.TryEquip(args.Target, uid, "mask", true); - if (!equipped) - return; - - component.EquipedOn = args.Target; - - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-user"), - args.Target, args.Target, PopupType.LargeCaution); - - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-mob", - ("entity", args.Target)), - uid, uid, PopupType.LargeCaution); - - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-others", - ("entity", args.Target)), args.Target, Filter.PvsExcept(uid), true, PopupType.Large); - - EntityManager.EnsureComponent(uid); - _stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(component.ParalyzeTime), true); - _damageableSystem.TryChangeDamage(args.Target, component.Damage, origin: args.Thrown); - } - - private void OnGotEquipped(EntityUid uid, FleshHuggerComponent component, GotEquippedEvent args) - { - if (args.Slot != "mask") - return; - component.EquipedOn = args.Equipee; - EntityManager.EnsureComponent(args.Equipee); - EntityManager.EnsureComponent(uid); - } - - private void OnGotEquippedHand(EntityUid uid, FleshHuggerComponent component, GotEquippedHandEvent args) - { - if (HasComp<_Sunrise.FleshCult.FleshPudgeComponent>(args.User)) - return; - if (HasComp(args.User)) - return; - if (component.IsDeath) - return; - _damageableSystem.TryChangeDamage(args.User, component.Damage); - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-bite-user"), - args.User, args.User); - } - - private void OnGotUnequipped(EntityUid uid, FleshHuggerComponent component, GotUnequippedEvent args) - { - if (args.Slot != "mask") - return; - if (HasComp(uid)) - EntityManager.RemoveComponent(uid); - if (HasComp(component.EquipedOn)) - EntityManager.RemoveComponent(args.Equipee); - _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(3), true); - component.EquipedOn = new EntityUid(); - } - - private void OnMeleeHit(EntityUid uid, FleshHuggerComponent component, MeleeHitEvent args) - { - if (!args.HitEntities.Any()) - return; - - foreach (var entity in args.HitEntities) - { - if (!HasComp(entity)) - return; - - if (TryComp(entity, out MobStateComponent? mobState)) - { - if (mobState.CurrentState is not MobState.Alive) - { - return; - } - } - - _inventory.TryGetSlotEntity(entity, "head", out var headItem); - if (HasComp(headItem)) - return; - - _inventory.TryGetSlotEntity(entity, "mask", out var maskItem); - if (HasComp(maskItem)) - return; - - var random = new Random(); - var shouldEquip = random.Next(1, 101) <= component.ChansePounce; - if (!shouldEquip) - return; - - _inventory.TryUnequip(entity, "head", true); - _inventory.TryUnequip(entity, "eyes", true); - _inventory.TryUnequip(entity, "mask", true); - - var equipped = _inventory.TryEquip(entity, uid, "mask", true); - if (!equipped) - return; - - component.EquipedOn = entity; - - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-user"), - entity, entity, PopupType.LargeCaution); - - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-hit-mob", ("entity", entity)), - uid, uid, PopupType.LargeCaution); - - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-others", - ("entity", entity)), entity, Filter.PvsExcept(entity), true, PopupType.Large); - EntityManager.EnsureComponent(uid); - _stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(component.ParalyzeTime), true); - _damageableSystem.TryChangeDamage(entity, component.Damage, origin: entity); - break; - } - } - - private static void OnMobStateChanged(EntityUid uid, FleshHuggerComponent component, MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Dead) - { - component.IsDeath = true; - } - } - - private void OnGetOff(EntityUid uid, FleshHuggerComponent component, FleshHuggerGetOffFromFaceActionEvent args) - { - if (args.Handled) - return; - - if (component.EquipedOn is not { Valid: true } targetId) - { - _popup.PopupEntity(Loc.GetString("flesh-worm-cant-get-off"), - uid, uid, PopupType.LargeCaution); - return; - } - - _inventory.TryUnequip(targetId, "mask", true, true); - component.EquipedOn = new EntityUid(); - - args.Handled = true; - } - - private void OnJump(EntityUid uid, FleshHuggerComponent component, FleshHuggerJumpActionEvent args) - { - if (args.Handled) - return; - - if (component.EquipedOn is { Valid: true }) - { - _popup.PopupEntity(Loc.GetString("flesh-worm-cant-jump"), - uid, uid, PopupType.LargeCaution); - return; - } - - args.Handled = true; - var xform = Transform(uid); - var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); - var direction = mapCoords.Position - xform.MapPosition.Position; - - _throwing.TryThrow(uid, direction, 7F, uid, 10F); - if (component.SoundJump != null) - { - _audioSystem.PlayPvs(component.SoundJump, uid, component.SoundJump.Params); - } - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - foreach (var comp in EntityQuery()) - { - comp.Accumulator += frameTime; - - if (comp.Accumulator <= comp.DamageFrequency) - continue; - - comp.Accumulator = 0; - - if (comp.EquipedOn is not { Valid: true } targetId) - continue; - if (HasComp(comp.EquipedOn)) - return; - if (TryComp(targetId, out MobStateComponent? mobState)) - { - if (mobState.CurrentState is not MobState.Alive) - { - _inventory.TryUnequip(targetId, "mask", true, true); - comp.EquipedOn = new EntityUid(); - return; - } - } - _damageableSystem.TryChangeDamage(targetId, comp.Damage); - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-user"), - targetId, targetId, PopupType.LargeCaution); - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-eat-face-others", - ("entity", targetId)), targetId, Filter.PvsExcept(targetId), true); - } - } - } -} diff --git a/Content.Server/_Sunrise/FleshCult/FleshMobSystem.cs b/Content.Server/_Sunrise/FleshCult/FleshMobSystem.cs deleted file mode 100644 index 65a614cfa7..0000000000 --- a/Content.Server/_Sunrise/FleshCult/FleshMobSystem.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Content.Shared._Sunrise.FleshCult; -using Content.Shared.Flesh; -using Content.Shared.Mobs; -using Robust.Server.GameObjects; -using Robust.Shared.Audio.Systems; - -namespace Content.Server._Sunrise.FleshCult -{ - public sealed class FleshMobSystem : SharedFleshMobSystem - { - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnMobStateChanged); - } - - private void OnMobStateChanged(EntityUid uid, FleshMobComponent component, MobStateChangedEvent args) - { - if (args.NewMobState != MobState.Dead) - return; - if (component.SoundDeath == null) - return; - _audioSystem.PlayPvs(component.SoundDeath, uid, component.SoundDeath.Params); - - var coords = _transformSystem.GetMapCoordinates(uid); - - for (var i = 0; i < component.DeathMobSpawnCount; i++) - { - Spawn(component.DeathMobSpawnId, coords); - } - } - } -} - diff --git a/Content.Server/_Sunrise/FleshCult/FleshPudgeComponent.cs b/Content.Server/_Sunrise/FleshCult/FleshPudgeComponent.cs deleted file mode 100644 index 1429612f05..0000000000 --- a/Content.Server/_Sunrise/FleshCult/FleshPudgeComponent.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server._Sunrise.FleshCult -{ - [RegisterComponent] - public sealed partial class FleshPudgeComponent : Component - { - [DataField("actionThrowWorm", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ActionThrowWormId = "FleshThrowWorm"; - - [DataField("actionAcidSpit", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ActionAcidSpitId = "FleshAcidSpit"; - - [DataField("actionAbsorbBloodPool", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ActionAbsorbBloodPoolId = "AbsorbBloodPool"; - - [ViewVariables(VVAccess.ReadWrite), DataField("soundThrowWorm")] - public SoundSpecifier? SoundThrowWorm = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/throw_worm.ogg"); - - [ViewVariables(VVAccess.ReadWrite), - DataField("faceHuggerMobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string FaceHuggerMobSpawnId = "MobFleshHugger"; - - [ViewVariables(VVAccess.ReadWrite), - DataField("bulletAcidSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string BulletAcidSpawnId = "BulletSplashAcid"; - - [DataField("healBloodAbsorbReagents")] public Solution HealBloodAbsorbReagents = new() - { - Contents = - { - new ReagentQuantity(new ReagentId("Carol", null), 1), - } - }; - - [ViewVariables(VVAccess.ReadWrite), DataField("bloodWhitelist")] - public List BloodWhitelist = new() - { - "Blood", - "CopperBlood", - "InsectBlood", - "AmmoniaBlood", - "ZombieBlood" - }; - - [DataField("bloodAbsorbSound")] - public SoundSpecifier BloodAbsorbSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg"); - } -} diff --git a/Content.Server/_Sunrise/FleshCult/FleshPudgeSystem.cs b/Content.Server/_Sunrise/FleshCult/FleshPudgeSystem.cs deleted file mode 100644 index 82e5630894..0000000000 --- a/Content.Server/_Sunrise/FleshCult/FleshPudgeSystem.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System.Linq; -using Content.Server.Actions; -using Content.Server.Popups; -using Content.Server.Weapons.Ranged.Systems; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Fluids.Components; -using Content.Shared.Popups; -using Content.Shared.Throwing; -using Robust.Server.GameObjects; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Collections; -using Robust.Shared.Prototypes; - -namespace Content.Server._Sunrise.FleshCult -{ - public sealed class FleshPudgeSystem : EntitySystem - { - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly ActionsSystem _action = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionSystem = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly GunSystem _gunSystem = default!; - [Dependency] private readonly PhysicsSystem _physics = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly TransformSystem _transformSystem = default!; - - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnThrowFaceHugger); - SubscribeLocalEvent(OnAbsorbBloodPoolActionEvent); - SubscribeLocalEvent(OnAcidSpit); - } - - private void OnThrowFaceHugger(EntityUid uid, FleshPudgeComponent component, FleshPudgeThrowFaceHuggerActionEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - var worm = Spawn(component.FaceHuggerMobSpawnId, Transform(uid).Coordinates); - var xform = Transform(uid); - var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); - var direction = mapCoords.Position - xform.MapPosition.Position; - - _throwing.TryThrow(worm, direction, 7F, uid, 10F); - if (component.SoundThrowWorm != null) - { - _audioSystem.PlayPvs(component.SoundThrowWorm, uid, component.SoundThrowWorm.Params); - } - _popupSystem.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-popup"), uid, PopupType.LargeCaution); - } - - private void OnAcidSpit(EntityUid uid, FleshPudgeComponent component, FleshPudgeAcidSpitActionEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - var acidBullet = Spawn(component.BulletAcidSpawnId, Transform(uid).Coordinates); - var xform = Transform(uid); - var mapCoords = args.Target.ToMap(_entityManager, _transformSystem); - var direction = mapCoords.Position - xform.MapPosition.Position; - var userVelocity = _physics.GetMapLinearVelocity(uid); - - _gunSystem.ShootProjectile(acidBullet, direction, userVelocity, uid, uid); - _audioSystem.PlayPvs(component.BloodAbsorbSound, uid, component.BloodAbsorbSound.Params); - } - - private void OnAbsorbBloodPoolActionEvent(EntityUid uid, FleshPudgeComponent component, - FleshPudgeAbsorbBloodPoolActionEvent args) - { - if (args.Handled) - return; - - var xform = Transform(uid); - var puddles = new ValueList<(EntityUid Entity, string Solution)>(); - puddles.Clear(); - foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, 1f)) - { - if (TryComp(entity, out var puddle)) - { - puddles.Add((entity, puddle.SolutionName)); - } - } - - if (puddles.Count == 0) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-not-find-puddles"), - uid, uid, PopupType.Large); - return; - } - - var absorbBlood = new Solution(); - foreach (var (puddle, solution) in puddles) - { - if (!_solutionSystem.TryGetSolution(puddle, solution, out var puddleSolution)) - { - continue; - } - foreach (var puddleSolutionContent in puddleSolution.Value.Comp.Solution.ToList()) - { - if (!component.BloodWhitelist.Contains(puddleSolutionContent.Reagent.Prototype)) - continue; - - var blood = puddleSolution.Value.Comp.Solution.SplitSolutionWithOnly( - puddleSolutionContent.Quantity, puddleSolutionContent.Reagent.Prototype); - - absorbBlood.AddSolution(blood, _prototypeManager); - } - - var ev = new SolutionContainerChangedEvent(puddleSolution.Value.Comp.Solution, solution); - RaiseLocalEvent(puddle, ref ev); - } - - if (absorbBlood.Volume == 0) - { - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-cant-absorb-puddle"), - uid, uid, PopupType.Large); - return; - } - - _audioSystem.PlayPvs(component.BloodAbsorbSound, uid, component.BloodAbsorbSound.Params); - _popupSystem.PopupEntity(Loc.GetString("flesh-cultist-absorb-puddle", ("Entity", uid)), - uid, uid, PopupType.Large); - - var transferSolution = new Solution(); - foreach (var solution in component.HealBloodAbsorbReagents) - { - transferSolution.AddReagent(solution.Reagent, solution.Quantity * (absorbBlood.Volume / 10)); - } - - if (_solutionSystem.TryGetInjectableSolution(uid, out var injectableSolution, out var _)) - { - _solutionSystem.TryAddSolution(injectableSolution.Value, transferSolution); - } - absorbBlood.RemoveAllSolution(); - args.Handled = true; - } - - private void OnStartup(EntityUid uid, FleshPudgeComponent component, ComponentStartup args) - { - _action.AddAction(uid, component.ActionAcidSpitId); - _action.AddAction(uid, component.ActionThrowWormId); - _action.AddAction(uid, component.ActionAbsorbBloodPoolId); - } - } -} diff --git a/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleComponent.cs b/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleComponent.cs index 2c0bc540c2..d7dcd76a66 100644 --- a/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleComponent.cs +++ b/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleComponent.cs @@ -16,8 +16,6 @@ public sealed partial class FleshCultRuleComponent : Component public SoundSpecifier AddedSound = new SoundPathSpecifier( "/Audio/_Sunrise/FleshCult/flesh_culstis_greeting.ogg"); - public Dictionary Cultists = new(); - [DataField("fleshCultistPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string FleshCultistPrototypeId = "FleshCultist"; @@ -27,21 +25,15 @@ public sealed partial class FleshCultRuleComponent : Component [DataField("faction", customTypeSerializer: typeof(PrototypeIdSerializer), required: true)] public string Faction = default!; + public List Cultists = new(); public int TotalCultists => Cultists.Count; public readonly List CultistsNames = new(); - public WinTypes WinType = WinTypes.Fail; - - public bool FleshHeartActive = false; - public Dictionary FleshHearts = new(); public EntityUid? TargetStation; - [DataField] - public List StarterItems = new() { "SyringeCarolNT", "SyringeCarolNT", "SyringeCarolNT" }; - public List SpeciesWhitelist = new() { "Human", @@ -51,16 +43,11 @@ public sealed partial class FleshCultRuleComponent : Component "Felinid", "Moth", "Swine", - "Arachnid" + "Arachnid", + "Demon", + "Vox", + "HumanoidXeno", + "Predator", + "Tajaran" }; - - public enum WinTypes - { - FleshHeartFinal, - AllCultistsDead, - Fail - } - - public TimeSpan AnnounceAt = TimeSpan.Zero; - public Dictionary StartCandidates = new(); } diff --git a/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleSystem.cs b/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleSystem.cs index 5fb2233b0f..0f411afc60 100644 --- a/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleSystem.cs +++ b/Content.Server/_Sunrise/FleshCult/GameRule/FleshCultRuleSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Server.Antag; +using Content.Server.Antag.Components; using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; @@ -12,9 +13,11 @@ using Content.Shared._Sunrise.FleshCult; using Content.Shared.FixedPoint; using Content.Shared.GameTicking.Components; using Content.Shared.Mind; +using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; using Content.Shared.Roles; using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; namespace Content.Server._Sunrise.FleshCult.GameRule; @@ -26,18 +29,54 @@ public sealed class FleshCultRuleSystem : GameRuleSystem [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; + + [ValidatePrototypeId] + private const string LeaderAntagProto = "FleshCultistLeader"; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(AfterEntitySelected); - - SubscribeLocalEvent(OnFleshHeartActivate); - SubscribeLocalEvent(OnFleshHeartDestruction); - SubscribeLocalEvent(OnFleshHeartFinal); - SubscribeLocalEvent(OnObjectivesTextPrepend); + SubscribeLocalEvent(OnAfterAntagSelectionComplete); + SubscribeLocalEvent(OnFleshHeartStatusChange); + } + + protected override void Started(EntityUid uid, + FleshCultRuleComponent component, + GameRuleComponent gameRule, + GameRuleStartedEvent args) + { + var eligible = new List>(); + var eligibleQuery = EntityQueryEnumerator(); + while (eligibleQuery.MoveNext(out var eligibleUid, out var eligibleComp, out var member)) + { + if (!_npcFaction.IsFactionHostile(component.Faction, (eligibleUid, member))) + continue; + + eligible.Add((eligibleUid, eligibleComp, member)); + } + + if (eligible.Count == 0) + return; + + component.TargetStation = RobustRandom.Pick(eligible); + } + + protected override void Ended(EntityUid uid, FleshCultRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + foreach (var componentFleshHeart in component.FleshHearts) + { + QueueDel(componentFleshHeart.Key); + } + + foreach (var cultist in component.Cultists) + { + RemComp(cultist); + } + QueueDel(uid); } private void OnObjectivesTextPrepend(EntityUid uid, FleshCultRuleComponent comp, ref ObjectivesTextPrependEvent args) @@ -48,121 +87,37 @@ public sealed class FleshCultRuleSystem : GameRuleSystem args.Text += "\n" + Loc.GetString("flesh-cult-round-end-leader", ("name", mind.CharacterName)!, ("username", session!.Name)); } - private void OnFleshHeartActivate(FleshHeartSystem.FleshHeartActivateEvent ev) + private void OnFleshHeartStatusChange(FleshCultSystem.FleshHeartStatusChangeEvent ev) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var fleshCult, out var gameRule)) { - if (!GameTicker.IsGameRuleAdded(uid, gameRule)) + switch (ev.Status) { - continue; - } - - if (ev.OwningStation == null) - { - return; - } - - if (fleshCult.TargetStation == null) - { - return; - } - - if (!TryComp(fleshCult.TargetStation, out StationDataComponent? data)) - { - return; - } - - foreach (var grid in data.Grids) - { - if (grid != ev.OwningStation) + case FleshHeartStatus.Base: { - continue; + fleshCult.FleshHearts.Add(ev.FleshHeartUid, FleshHeartStatus.Base); + break; } - - fleshCult.FleshHearts.Add(ev.FleshHeardUid, FleshHeartStatus.Active); - fleshCult.FleshHeartActive = true; - return; - } - } - } - - private void OnFleshHeartDestruction(FleshHeartSystem.FleshHeartDestructionEvent ev) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var fleshCult, out var gameRule)) - { - if (!GameTicker.IsGameRuleAdded(uid, gameRule)) - { - continue; - } - - if (ev.OwningStation == null) - { - return; - } - - if (fleshCult.TargetStation == null) - { - return; - } - - if (!TryComp(fleshCult.TargetStation, out StationDataComponent? data)) - { - return; - } - - foreach (var grid in data.Grids) - { - if (grid != ev.OwningStation) + case FleshHeartStatus.Active: { - continue; + if (fleshCult.FleshHearts.ContainsKey(ev.FleshHeartUid)) + fleshCult.FleshHearts[ev.FleshHeartUid] = FleshHeartStatus.Active; + break; } - - if (fleshCult.FleshHearts.ContainsKey(ev.FleshHeardUid)) + case FleshHeartStatus.Destruction: { - fleshCult.FleshHearts[ev.FleshHeardUid] = FleshHeartStatus.Destruction; + if (fleshCult.FleshHearts.ContainsKey(ev.FleshHeartUid)) + fleshCult.FleshHearts[ev.FleshHeartUid] = FleshHeartStatus.Destruction; + break; } - fleshCult.FleshHeartActive = false; - return; - } - } - } - - private void OnFleshHeartFinal(FleshHeartSystem.FleshHeartFinalEvent ev) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var fleshCult, out var gameRule)) - { - if (!GameTicker.IsGameRuleAdded(uid, gameRule)) - { - continue; - } - - if (ev.OwningStation == null) - { - return; - } - - if (fleshCult.TargetStation == null) - { - return; - } - - if (!TryComp(fleshCult.TargetStation, out StationDataComponent? data)) - { - return; - } - foreach (var grid in data.Grids) - { - if (grid != ev.OwningStation) + case FleshHeartStatus.Final: { - continue; + if (fleshCult.FleshHearts.ContainsKey(ev.FleshHeartUid)) + fleshCult.FleshHearts[ev.FleshHeartUid] = FleshHeartStatus.Final; + _roundEndSystem.EndRound(); + break; } - - fleshCult.WinType = FleshCultRuleComponent.WinTypes.FleshHeartFinal; - _roundEndSystem.EndRound(); - return; } } } @@ -172,10 +127,32 @@ public sealed class FleshCultRuleSystem : GameRuleSystem MakeCultist(args.EntityUid, 15, ent.Comp); } - public void MakeCultistAdmin(EntityUid target, FixedPoint2 startingPoints) + private void OnAfterAntagSelectionComplete(Entity ent, ref AntagSelectionCompleteEvent args) { - var fleshCultRule = StartGameRule(); - MakeCultist(target, startingPoints, fleshCultRule); + var leader = GetLeader(args.GameRule); + if (leader == null || !_mindSystem.TryGetMind(leader.Value, out var mindId, out var mind)) + return; + ent.Comp.CultistsLeaderMind = mindId; + } + + private EntityUid? GetLeader(Entity antagSelection) + { + EntityUid? leader = null; + foreach (var compSelectedMind in antagSelection.Comp.SelectedMinds) + { + if (!TryComp(compSelectedMind.Item1, out var mindComp)) + continue; + + foreach (var roleInfo in _roles.MindGetAllRoleInfo((compSelectedMind.Item1, mindComp))) + { + if (roleInfo.Prototype != LeaderAntagProto || mindComp.CurrentEntity == null) + continue; + + leader = mindComp.CurrentEntity.Value; + } + } + + return leader; } public FleshCultRuleComponent StartGameRule() @@ -216,6 +193,8 @@ public sealed class FleshCultRuleSystem : GameRuleSystem _store.TryAddCurrency(new Dictionary { {fleshCultistComponent.StolenCurrencyPrototype, startingPoints} }, mind.OwnedEntity.Value); + fleshCultRule.Cultists.Add(fleshCultist); + return true; } @@ -282,7 +261,14 @@ public sealed class FleshCultRuleSystem : GameRuleSystem } } - if (component.FleshHeartActive) + var fleshHeartActive = false; + foreach (var fleshCultFleshHeart in component.FleshHearts) + { + if (fleshCultFleshHeart.Value is FleshHeartStatus.Active or FleshHeartStatus.Final) + fleshHeartActive = true; + } + + if (fleshHeartActive) { result += "\n" + Loc.GetString("flesh-cult-round-end-flesh-heart-succes"); } diff --git a/Content.Shared/CCVar/CCVars.Game.cs b/Content.Shared/CCVar/CCVars.Game.cs index 4ec9edade8..54e47df322 100644 --- a/Content.Shared/CCVar/CCVars.Game.cs +++ b/Content.Shared/CCVar/CCVars.Game.cs @@ -101,7 +101,7 @@ public sealed partial class CCVars /// If roles should be restricted based on time. /// public static readonly CVarDef - GameRoleTimers = CVarDef.Create("game.role_timers", true, CVar.SERVER | CVar.REPLICATED); + GameRoleTimers = CVarDef.Create("game.role_timers", false, CVar.SERVER | CVar.REPLICATED); // Sunrise-Edit /// /// Override default role requirements using a diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index b39a1091b2..20ddee539a 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -175,6 +175,7 @@ public partial class ListingData : IEquatable /// The event that is broadcast when the listing is purchased. /// [DataField] + [NonSerialized] // Sunrise-Edit public object? ProductEvent; [DataField] diff --git a/Content.Shared/Whitelist/EntityWhitelist.cs b/Content.Shared/Whitelist/EntityWhitelist.cs index cbe4633360..ce99009061 100644 --- a/Content.Shared/Whitelist/EntityWhitelist.cs +++ b/Content.Shared/Whitelist/EntityWhitelist.cs @@ -1,3 +1,4 @@ +using Content.Shared.Humanoid.Prototypes; using Content.Shared.Item; using Content.Shared.Tag; using Robust.Shared.Prototypes; @@ -53,6 +54,10 @@ public sealed partial class EntityWhitelist [DataField] public List>? Tags; + // Sunrise-Start + public List>? Species; + // Sunrise-End + /// /// If false, an entity only requires one of these components or tags to pass the whitelist. If true, an /// entity requires to have ALL of these components and tags to pass. diff --git a/Content.Shared/Whitelist/EntityWhitelistSystem.cs b/Content.Shared/Whitelist/EntityWhitelistSystem.cs index 9a6e87c1b4..fd615331c2 100644 --- a/Content.Shared/Whitelist/EntityWhitelistSystem.cs +++ b/Content.Shared/Whitelist/EntityWhitelistSystem.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared.Humanoid; using Content.Shared.Item; using Content.Shared.Roles; using Content.Shared.Tag; @@ -100,6 +101,11 @@ public sealed class EntityWhitelistSystem : EntitySystem : _tag.HasAnyTag(uid, list.Tags); } + // Sunrise-Start + if (list.Species != null && TryComp(uid, out var appearance)) + return list.Species.Contains(appearance.Species); + // Sunrise-End + return list.RequireAll; } /// The following are a list of "helper functions" that are basically the same as each other diff --git a/Content.Shared/_Sunrise/FleshCult/FleshCultistComponent.cs b/Content.Shared/_Sunrise/FleshCult/FleshCultistComponent.cs index cf88c79508..3e98385127 100644 --- a/Content.Shared/_Sunrise/FleshCult/FleshCultistComponent.cs +++ b/Content.Shared/_Sunrise/FleshCult/FleshCultistComponent.cs @@ -1,6 +1,4 @@ using Content.Shared.Alert; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Content.Shared.StatusIcon; using Content.Shared.Store; @@ -17,116 +15,24 @@ public sealed partial class FleshCultistComponent : Component [ViewVariables(VVAccess.ReadWrite)] public FixedPoint2 Hunger = 100; [ViewVariables(VVAccess.ReadWrite), DataField("hungerСonsumption")] - public FixedPoint2 HungerСonsumption = -0.05; // 200 hunger in 60 minutes + public FixedPoint2 HungerСonsumption = -0.025; // 100 hunger in 60 minutes [ViewVariables(VVAccess.ReadWrite), DataField("maxHunger")] - public FixedPoint2 MaxHunger = 200; + public FixedPoint2 MaxHunger = 100; - [ViewVariables(VVAccess.ReadWrite), - DataField("bulletAcidSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string BulletAcidSpawnId = "BulletSplashAcid"; + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ActionFleshCultistShop = "FleshCultistShop"; - [ViewVariables(VVAccess.ReadWrite), DataField("speciesWhitelist")] - public List SpeciesWhitelist = new() - { - "Human", - "Reptilian", - "Dwarf", - "Vulpkanin", - "Felinid", - "Moth", - "Swine", - "Arachnid", - }; - - [DataField("adrenalinReagents")] public Solution AdrenalinReagents = new() - { - Contents = { new ReagentQuantity(new ReagentId("Ephedrine", null), 10) } - }; - - [DataField("healDevourReagents")] public Solution HealDevourReagents = new() - { - Contents = - { - new ReagentQuantity(new ReagentId("Carol", null), 20), - } - }; - - [DataField("healBloodAbsorbReagents")] public Solution HealBloodAbsorbReagents = new() - { - Contents = - { - new ReagentQuantity(new ReagentId("Carol", null), 1), - } - }; - - [DataField("bloodAbsorbSound")] - public SoundSpecifier BloodAbsorbSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg"); - - [ViewVariables(VVAccess.ReadWrite), DataField("bloodWhitelist")] - public List BloodWhitelist = new() - { - "Blood", - "CopperBlood", - "InsectBlood", - "AmmoniaBlood", - "ZombieBlood" - }; - - [DataField("devourTime")] public float DevourTime = 10f; - - [DataField("devourSound")] - public SoundSpecifier DevourSound = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/devour_flesh_cultist.ogg"); + [DataField] + public EntityUid? ActionFleshCultistShopEntity; [DataField("stolenCurrencyPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] public string StolenCurrencyPrototype = "StolenMutationPoint"; - [ViewVariables(VVAccess.ReadWrite), - DataField("fleshBladeSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string BladeSpawnId = "FleshBlade"; - - [ViewVariables(VVAccess.ReadWrite), - DataField("fleshFistSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string FistSpawnId = "FleshFist"; - - [ViewVariables(VVAccess.ReadWrite), - DataField("clawSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ClawSpawnId = "FleshClaw"; - - [ViewVariables(VVAccess.ReadWrite), - DataField("spikeHandGunSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string SpikeHandGunSpawnId = "FleshSpikeHandGun"; - - [ViewVariables(VVAccess.ReadWrite), - DataField("armorSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ArmorSpawnId = "ClothingOuterArmorFlesh"; - - [ViewVariables(VVAccess.ReadWrite), - DataField("heavyArmorSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string HeavyArmorSpawnId = "ClothingOuterHeavyArmorFlesh"; - - [ViewVariables(VVAccess.ReadWrite), - DataField("spiderLegsSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string SpiderLegsSpawnId = "ClothingFleshSpiderLegs"; - [ViewVariables(VVAccess.ReadWrite), DataField("fleshMutationMobId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string FleshMutationMobId = "MobFleshPudge"; - [ViewVariables(VVAccess.ReadWrite), DataField("soundMutation")] - public SoundSpecifier SoundMutation = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/flesh_cultist_mutation.ogg"); - - [DataField("fleshHeartId", customTypeSerializer: typeof(PrototypeIdSerializer)), - ViewVariables(VVAccess.ReadWrite)] - public string FleshHeartId = "FleshHeart"; - - [ViewVariables(VVAccess.ReadWrite), DataField("soundThrowWorm")] - public SoundSpecifier? SoundThrowHugger = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/throw_worm.ogg"); - - [ViewVariables(VVAccess.ReadWrite), - DataField("huggerMobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string HuggerMobSpawnId = "MobFleshHugger"; - public SoundSpecifier BuySuccesSound = new SoundPathSpecifier( "/Audio/_Sunrise/FleshCult/flesh_cultist_buy_succes.ogg"); @@ -139,4 +45,7 @@ public sealed partial class FleshCultistComponent : Component [DataField] public ProtoId MutationPointAlert = "MutationPoint"; + + [DataField] + public SoundSpecifier SoundMutation = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/flesh_cultist_mutation.ogg"); } diff --git a/Content.Shared/_Sunrise/FleshCult/FleshHeartComponent.cs b/Content.Shared/_Sunrise/FleshCult/FleshHeartComponent.cs index f41bf9202b..352d0468a7 100644 --- a/Content.Shared/_Sunrise/FleshCult/FleshHeartComponent.cs +++ b/Content.Shared/_Sunrise/FleshCult/FleshHeartComponent.cs @@ -11,36 +11,19 @@ namespace Content.Shared.Flesh { [DataField("transformSound")] public SoundSpecifier TransformSound = new SoundCollectionSpecifier("gib"); - [ViewVariables(VVAccess.ReadWrite), DataField("speciesWhitelist")] - public List SpeciesWhitelist = new() - { - "Human", - "Reptilian", - "Dwarf", - "Vulpkanin", - "Felinid", - "Moth", - "Swine", - "Arachnid", - }; - [ViewVariables(VVAccess.ReadWrite)] [DataField("entryDelay")] public float EntryDelay = 10f; public Container BodyContainer = default!; - [DataField("alertLevelOnActivate")] public string AlertLevelOnActivate = "gamma"; - - [DataField("alertLevelOnDeactivate")] public string AlertLevelOnDeactivate = "green"; - public EntityUid? AmbientAudioStream = default; [DataField("bodyToFinalStage"), ViewVariables(VVAccess.ReadWrite)] public int BodyToFinalStage = 3; // default 3 [DataField("timeLiveFinalHeartToWin"), ViewVariables(VVAccess.ReadWrite)] - public int TimeLiveFinalHeartToWin = 900; // default 600 + public int TimeLiveFinalHeartToWin = 600; // default 600 [DataField("spawnObjectsFrequency"), ViewVariables(VVAccess.ReadWrite)] public float SpawnObjectsFrequency = 60; @@ -59,7 +42,7 @@ namespace Content.Shared.Flesh public Dictionary Spawns = new(); [DataField("spawnMobsFrequency"), ViewVariables(VVAccess.ReadWrite)] - public float SpawnMobsFrequency = 120; + public float SpawnMobsFrequency = 100; [DataField("spawnMobsAmount"), ViewVariables(VVAccess.ReadWrite)] public int SpawnMobsAmount = 10; @@ -80,7 +63,7 @@ namespace Content.Shared.Flesh public float FinalStageAccumulator = 0; [ViewVariables] - public HeartStates State = HeartStates.Base; + public HeartStatus Status = HeartStatus.Base; public readonly HashSet EdgeMobs = new(); @@ -93,7 +76,7 @@ namespace Content.Shared.Flesh } } -public enum HeartStates +public enum HeartStatus { Base, Active, diff --git a/Content.Shared/_Sunrise/FleshCult/FleshHeartVisuals.cs b/Content.Shared/_Sunrise/FleshCult/FleshHeartVisuals.cs index 179f76097d..7e056816ad 100644 --- a/Content.Shared/_Sunrise/FleshCult/FleshHeartVisuals.cs +++ b/Content.Shared/_Sunrise/FleshCult/FleshHeartVisuals.cs @@ -11,8 +11,9 @@ public enum FleshHeartVisuals : byte [Serializable, NetSerializable] public enum FleshHeartStatus { + Base, Active, - Disable, + Final, Destruction } diff --git a/Content.Shared/_Sunrise/FleshCult/FleshHuggerComponent.cs b/Content.Shared/_Sunrise/FleshCult/FleshHuggerComponent.cs index 1ae3814763..792958ff6e 100644 --- a/Content.Shared/_Sunrise/FleshCult/FleshHuggerComponent.cs +++ b/Content.Shared/_Sunrise/FleshCult/FleshHuggerComponent.cs @@ -1,5 +1,4 @@ -using Content.Server.Flesh; -using Content.Shared.Damage; +using Content.Shared.Damage; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -7,7 +6,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Shared._Sunrise.FleshCult { - [Access(typeof(SharedFleshHuggerSystem))] [RegisterComponent, NetworkedComponent] public sealed partial class FleshHuggerComponent : Component { diff --git a/Content.Shared/_Sunrise/FleshCult/FleshMobComponent.cs b/Content.Shared/_Sunrise/FleshCult/FleshMobComponent.cs index 530dc041ad..0fdac880f8 100644 --- a/Content.Shared/_Sunrise/FleshCult/FleshMobComponent.cs +++ b/Content.Shared/_Sunrise/FleshCult/FleshMobComponent.cs @@ -1,12 +1,12 @@ -using Content.Shared.Actions; -using Content.Shared.StatusIcon; +using Content.Shared.StatusIcon; using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared._Sunrise.FleshCult { - [RegisterComponent] + [RegisterComponent, NetworkedComponent, AutoGenerateComponentPause] public sealed partial class FleshMobComponent : Component { [ViewVariables(VVAccess.ReadWrite), DataField("soundDeath")] @@ -22,21 +22,16 @@ namespace Content.Shared._Sunrise.FleshCult [DataField("fleshStatusIcon")] public ProtoId StatusIcon { get; set; } = "FleshFaction"; + [DataField] + public TimeSpan PopupCooldown = TimeSpan.FromSeconds(3.0); + + [DataField] + [AutoPausedField] + public TimeSpan? NextPopupTime; + + [DataField] + public EntityUid? LastAttackedEntity; + public bool IsDeath = false; } } - -public sealed partial class FleshPudgeThrowFaceHuggerActionEvent : WorldTargetActionEvent -{ - -} - -public sealed partial class FleshPudgeAcidSpitActionEvent : WorldTargetActionEvent -{ - -} - -public sealed partial class FleshPudgeAbsorbBloodPoolActionEvent : InstantActionEvent -{ - -} diff --git a/Content.Shared/_Sunrise/FleshCult/SharedFleshCultist.cs b/Content.Shared/_Sunrise/FleshCult/SharedFleshCultist.cs index b5119b4d5e..dfd66035da 100644 --- a/Content.Shared/_Sunrise/FleshCult/SharedFleshCultist.cs +++ b/Content.Shared/_Sunrise/FleshCult/SharedFleshCultist.cs @@ -1,52 +1,31 @@ using Content.Shared.Actions; using Content.Shared.DoAfter; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared._Sunrise.FleshCult; -[Serializable, NetSerializable] -public sealed partial class FleshCultistDevourDoAfterEvent : SimpleDoAfterEvent +public sealed partial class FleshCultistInsulatedImmunityMutationEvent : InstantActionEvent { } -[Serializable, NetSerializable] -public sealed partial class FleshCultistInfectionDoAfterEvent : SimpleDoAfterEvent +public sealed partial class FleshCultistPressureImmunityMutationEvent : InstantActionEvent { } -[Serializable, NetSerializable] -public sealed partial class FleshCultistInsulatedImmunityMutationEvent : SimpleDoAfterEvent +public sealed partial class FleshCultistFlashImmunityMutationEvent : InstantActionEvent { } -[Serializable, NetSerializable] -public sealed partial class FleshCultistPressureImmunityMutationEvent : SimpleDoAfterEvent +public sealed partial class FleshCultistRespiratorImmunityMutationEvent : InstantActionEvent { } -[Serializable, NetSerializable] -public sealed partial class FleshCultistFlashImmunityMutationEvent : SimpleDoAfterEvent -{ - -} - -[Serializable, NetSerializable] -public sealed partial class FleshCultistRespiratorImmunityMutationEvent : SimpleDoAfterEvent -{ - -} - -[Serializable, NetSerializable] -public sealed partial class FleshCultistColdTempImmunityMutationEvent : SimpleDoAfterEvent -{ - -} - -public sealed partial class FleshCultistAcidSpitActionEvent : WorldTargetActionEvent +public sealed partial class FleshCultistColdTempImmunityMutationEvent : InstantActionEvent { } @@ -56,43 +35,6 @@ public sealed partial class FleshCultistShopActionEvent : InstantActionEvent } -public sealed partial class FleshCultistBladeActionEvent : InstantActionEvent -{ - -} - - -public sealed partial class FleshCultistClawActionEvent : InstantActionEvent -{ - -} - -public sealed partial class FleshCultistFistActionEvent : InstantActionEvent -{ - -} - -public sealed partial class FleshCultistSpikeHandGunActionEvent : InstantActionEvent -{ - -} - -public sealed partial class FleshCultistArmorActionEvent : InstantActionEvent -{ - -} - -public sealed partial class FleshCultistHeavyArmorActionEvent : InstantActionEvent -{ - -} - -public sealed partial class FleshCultistSpiderLegsActionEvent : InstantActionEvent -{ - -} - - public sealed partial class FleshCultistAdrenalinActionEvent : InstantActionEvent { @@ -103,11 +45,6 @@ public sealed partial class FleshCultistCreateFleshHeartActionEvent : InstantAct } -public sealed partial class FleshCultistThrowHuggerActionEvent : WorldTargetActionEvent -{ - -} - public sealed partial class FleshCultistAbsorbBloodPoolActionEvent : InstantActionEvent { @@ -118,5 +55,40 @@ public sealed partial class FleshCultistDevourActionEvent : EntityTargetActionEv } +public sealed partial class FleshCultistThrowHuggerActionEvent : WorldTargetActionEvent +{ +} + +public sealed partial class FleshCultistAcidSpitActionEvent : WorldTargetActionEvent +{ + +} + +[Serializable, NetSerializable] +public sealed partial class FleshCultistDevourDoAfterEvent : SimpleDoAfterEvent +{ + +} +public sealed partial class FleshCultistHandTransformEvent : InstantActionEvent +{ + [DataField] + public EntProtoId Prototype; +} + +public sealed partial class FleshCultistBodyTransformEvent : InstantActionEvent +{ + [DataField] + public EntProtoId Prototype; + [DataField] + public string TargetSlot = string.Empty; + [DataField] + public List CheckSlots = []; +} + +public sealed partial class FleshCultistUnlockAbilityEvent : InstantActionEvent +{ + [DataField] + public EntProtoId Prototype = string.Empty; +} diff --git a/Content.Shared/_Sunrise/FleshCult/SharedFleshCultistSystem.cs b/Content.Shared/_Sunrise/FleshCult/SharedFleshCultistSystem.cs deleted file mode 100644 index fa95472b09..0000000000 --- a/Content.Shared/_Sunrise/FleshCult/SharedFleshCultistSystem.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Shared._Sunrise.FleshCult; - -public abstract class SharedFleshCultistSystem : EntitySystem -{ - - public override void Initialize() - { - base.Initialize(); - - } -} diff --git a/Content.Shared/_Sunrise/FleshCult/SharedFleshHuggerSystem.cs b/Content.Shared/_Sunrise/FleshCult/SharedFleshHuggerSystem.cs index f57ebca390..648d85ab9e 100644 --- a/Content.Shared/_Sunrise/FleshCult/SharedFleshHuggerSystem.cs +++ b/Content.Shared/_Sunrise/FleshCult/SharedFleshHuggerSystem.cs @@ -3,29 +3,30 @@ using Content.Shared.Actions; using Content.Shared.Inventory.Events; using Content.Shared.Popups; -namespace Content.Server.Flesh +namespace Content.Shared._Sunrise.FleshCult; + +public sealed class SharedFleshHuggerSystem : EntitySystem { - public class SharedFleshHuggerSystem : EntitySystem + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() { - [Dependency] private readonly SharedPopupSystem _popup = default!; + SubscribeLocalEvent(OnUnequipAttempt); + } - public override void Initialize() - { - SubscribeLocalEvent(OnUnequipAttempt); - } - - private void OnUnequipAttempt(EntityUid uid, FleshHuggerComponent component, BeingUnequippedAttemptEvent args) - { - if (args.Slot != "mask") - return; - if (component.EquipedOn != args.Unequipee) - return; - if (HasComp(args.Unequipee)) - return; - _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-try-unequip"), - args.Unequipee, args.Unequipee, PopupType.Large); - args.Cancel(); - } + private void OnUnequipAttempt(EntityUid uid, FleshHuggerComponent component, BeingUnequippedAttemptEvent args) + { + if (args.Slot != "mask") + return; + if (component.EquipedOn != args.Unequipee) + return; + if (HasComp(args.Unequipee)) + return; + _popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-try-unequip"), + args.Unequipee, + args.Unequipee, + PopupType.Large); + args.Cancel(); } } diff --git a/Content.Shared/_Sunrise/FleshCult/SharedFleshMobSystem.cs b/Content.Shared/_Sunrise/FleshCult/SharedFleshMobSystem.cs index bf59ce3507..3af6feb90b 100644 --- a/Content.Shared/_Sunrise/FleshCult/SharedFleshMobSystem.cs +++ b/Content.Shared/_Sunrise/FleshCult/SharedFleshMobSystem.cs @@ -1,12 +1,15 @@ using Content.Shared.Flesh; +using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; using Content.Shared.Popups; +using Robust.Shared.Timing; namespace Content.Shared._Sunrise.FleshCult; -public abstract class SharedFleshMobSystem : EntitySystem +public sealed class SharedFleshMobSystem : EntitySystem { [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { @@ -15,30 +18,40 @@ public abstract class SharedFleshMobSystem : EntitySystem SubscribeLocalEvent(OnAttackAttempt); } - private void OnAttackAttempt(EntityUid uid, FleshMobComponent component, AttackAttemptEvent args) + private void OnAttackAttempt(Entity fleshMob, ref AttackAttemptEvent args) { - if (args.Cancelled) + if (args.Cancelled || args.Target == null) return; if (HasComp(args.Target)) { - _popup.PopupCursor(Loc.GetString("flesh-mob-cant-atack-flesh-mob"), uid, - PopupType.LargeCaution); + ShowPopup(fleshMob, args.Target.Value, Loc.GetString("flesh-mob-cant-atack-flesh-mob")); args.Cancel(); } - if (HasComp<_Sunrise.FleshCult.FleshCultistComponent>(args.Target)) + + if (HasComp(args.Target)) { - _popup.PopupCursor(Loc.GetString("flesh-mob-cant-atack-flesh-cultist"), uid, - PopupType.LargeCaution); + ShowPopup(fleshMob, args.Target.Value, Loc.GetString("flesh-mob-cant-atack-flesh-cultist")); args.Cancel(); } if (HasComp(args.Target)) { - _popup.PopupCursor(Loc.GetString("flesh-mob-cant-atack-flesh-heart"), uid, - PopupType.LargeCaution); + ShowPopup(fleshMob, args.Target.Value, Loc.GetString("flesh-mob-cant-atack-flesh-heart")); args.Cancel(); } } + private void ShowPopup(Entity user, EntityUid target, string reason) + { + if (target == user.Comp.LastAttackedEntity + && !(_timing.CurTime > user.Comp.NextPopupTime)) + return; + + var targetName = Identity.Entity(target, EntityManager); + _popup.PopupCursor(Loc.GetString(reason, ("entity", targetName)), user, PopupType.LargeCaution); + user.Comp.NextPopupTime = _timing.CurTime + user.Comp.PopupCooldown; + user.Comp.LastAttackedEntity = target; + } + } diff --git a/Resources/Locale/ru-RU/_strings/_sunrise/flesh-cult/fleshcultist.ftl b/Resources/Locale/ru-RU/_strings/_sunrise/flesh-cult/fleshcultist.ftl index d5fcde5618..9c45288c1a 100644 --- a/Resources/Locale/ru-RU/_strings/_sunrise/flesh-cult/fleshcultist.ftl +++ b/Resources/Locale/ru-RU/_strings/_sunrise/flesh-cult/fleshcultist.ftl @@ -1,15 +1,9 @@ -flesh-cultist-transform-hand-in-blade = Рука { CAPITALIZE($Entity) } превращается в клинок из плоти. -flesh-cultist-transform-hand-in-claw = Рука { CAPITALIZE($Entity) } превращается в клешню из плоти. -flesh-cultist-transform-hand-in-spike-gun = Рука { CAPITALIZE($Entity) } превращается в шипострел из плоти. -flesh-cultist-transform-blade-in-hand = Клинок из плоти { CAPITALIZE($Entity) } превращается обратно в руку. -flesh-cultist-transform-claw-in-hand = Клешня из плоти { CAPITALIZE($Entity) } превращается обратно в руку. -flesh-cultist-transform-spike-gun-in-hand = Шипострел из плоти { CAPITALIZE($Entity) } превращается обратно в руку. -flesh-cultist-transform-armor-on = { CAPITALIZE($Entity) } покрывается броней из плоти. -flesh-cultist-transform-armor-off = { CAPITALIZE($Entity) } убирает свою броню из плоти. -flesh-cultist-transform-spider-legs-on = Ноги { CAPITALIZE($Entity) } превращаются в паучьи лапы из плоти. -flesh-cultist-transform-spider-legs-off = Паучьи лапы { CAPITALIZE($Entity) } превращаются обратно в человеческие ноги. +flesh-cultist-transform-hand-to-mod = Рука { CAPITALIZE( $User ) } превращается в { CAPITALIZE( $Mod ) }. +flesh-cultist-transform-mod-to-hand = { CAPITALIZE( $Mod ) } { CAPITALIZE( $User ) } превращается в обратно в руку. +flesh-cultist-transform-body-add = { CAPITALIZE( $User ) } отращивает { CAPITALIZE( $Mod ) }. +flesh-cultist-transform-body-remove = { CAPITALIZE( $User ) } скрывает { CAPITALIZE( $Mod ) }. flesh-cultist-transform-user-hand-blocked = Данная рука занята другой модификацией. -flesh-cultist-transform-armor-blocked = Вы не можете использовать паучьи ноги и броню одновременно. +flesh-cultist-transform-conflict = Вы не можете использовать это сейчас. flesh-cultist-transform-spider-legs-blocked = Вы не можете использовать броню с паучьими лапами. flesh-cultist-infection-target-critical = Вы не можете заразить умирающее существо. flesh-cultist-infection-target-dead = Вы не можете заразить мертвое существо. @@ -49,6 +43,8 @@ flesh-cultist-spike-gun-name = Рука шипострел flesh-cultist-spike-gun-desc = Превращает активную руку в смертоносный шипострел из плоти. flesh-cultist-armor-name = Броня из плоти flesh-cultist-armor-desc = Облачает вас в броню из плоти и костей. +flesh-cultist-heavy-armor-name = Тяжелая броня из плоти +flesh-cultist-heavy-armor-desc = Облачает вас в тяжелую броню из плоти и костей. flesh-cultist-spider-legs-name = Паучий облик flesh-cultist-spider-legs-desc = Превращает часть вашего тела в паучий вид, давая небольшую защиту и большой прирост скорости. flesh-cultist-absorb-blood-pool-name = Поглощение лужи крови @@ -83,11 +79,13 @@ flesh-cultist-adrenaline-evolution-desc = Получите возможност flesh-cultist-throw-hugger-evolution-name = Бросок лицехвата flesh-cultist-throw-hugger-evolution-desc = Получите возможность создать и кинуть лицехвата из плоти, который будет цеплятся за лица врагов. flesh-cultist-acid-spit-evolution-name = Кислотный плевок -flesh-cultist-acid-spit-evolution-desc = Получите возможность преваться кислотой в ваших врагов. +flesh-cultist-acid-spit-evolution-desc = Получите возможность плеваться кислотой в ваших врагов. flesh-cultist-create-flesh-heart-evolution-name = Создание сердца из плоти flesh-cultist-create-flesh-heart-evolution-desc = Получите возможность создать сердце из плоти. Его создание и пробуждение - ваша ключевая задача на станции. Для пробуждения потребуется три тела развитых существ из плоти. После активации будьте готовы защищать его от любой угрозы. +flesh-cultist-absorb-blood-pool-evolution-name = Поглощение лужи крови +flesh-cultist-absorb-blood-pool-evolution-desc = Позволяет поглотить лужи крови для лечения. flesh-cultist-insulated-immunity-evolution-name = Сопротивление flesh-cultist-insulated-immunity-evolution-desc = Вы сможете спокойно работать с электричеством не опасаясь поражения напряжением. diff --git a/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_cultist.yml b/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_cultist.yml index e6ad8db16b..c90209b1f9 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_cultist.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_cultist.yml @@ -17,7 +17,8 @@ components: - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistBlade.png - event: !type:FleshCultistBladeActionEvent + event: !type:FleshCultistHandTransformEvent + prototype: FleshBlade itemIconStyle: NoItem useDelay: 10 checkCanInteract: false @@ -55,7 +56,8 @@ components: - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistClaw.png - event: !type:FleshCultistClawActionEvent + event: !type:FleshCultistHandTransformEvent + prototype: FleshClaw itemIconStyle: NoItem useDelay: 10 checkCanInteract: false @@ -96,7 +98,8 @@ components: - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistFist.png - event: !type:FleshCultistFistActionEvent + event: !type:FleshCultistHandTransformEvent + prototype: FleshFist itemIconStyle: NoItem useDelay: 10 checkCanInteract: false @@ -109,7 +112,8 @@ components: - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistSpikeGun.png - event: !type:FleshCultistSpikeHandGunActionEvent + event: !type:FleshCultistHandTransformEvent + prototype: FleshSpikeHandGun itemIconStyle: NoItem useDelay: 10 checkCanInteract: false @@ -122,7 +126,11 @@ components: - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistArmor.png - event: !type:FleshCultistArmorActionEvent + event: !type:FleshCultistBodyTransformEvent + prototype: ClothingOuterArmorFlesh + targetSlot: outerClothing + checkSlots: + - shoes itemIconStyle: NoItem useDelay: 30 checkCanInteract: false @@ -135,7 +143,11 @@ components: - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistHeavyArmor.png - event: !type:FleshCultistHeavyArmorActionEvent + event: !type:FleshCultistBodyTransformEvent + prototype: ClothingOuterHeavyArmorFlesh + targetSlot: outerClothing + checkSlots: + - shoes itemIconStyle: NoItem useDelay: 30 checkCanInteract: false @@ -148,7 +160,11 @@ components: - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistSpiderLegs.png - event: !type:FleshCultistSpiderLegsActionEvent + event: !type:FleshCultistBodyTransformEvent + prototype: ClothingFleshSpiderLegs + targetSlot: shoes + checkSlots: + - outerClothing itemIconStyle: NoItem useDelay: 30 checkCanInteract: false diff --git a/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_pudge.yml b/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_pudge.yml deleted file mode 100644 index 52b2710d6b..0000000000 --- a/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_pudge.yml +++ /dev/null @@ -1,38 +0,0 @@ -- type: entity - id: FleshThrowWorm - name: flesh-pudge-throw-hugger-name - description: flesh-pudge-throw-hugger-desc - categories: [ HideSpawnMenu ] - components: - - type: WorldTargetAction - icon: _Sunrise/FleshCult/Interface/Actions/fleshThrowHugger.png - itemIconStyle: NoItem - checkCanAccess: false - range: 200 - event: !type:FleshPudgeThrowFaceHuggerActionEvent - useDelay: 240 - -- type: entity - id: FleshAcidSpit - name: flesh-cultist-acid-spit-name - description: flesh-cultist-acid-spit-desc - categories: [ HideSpawnMenu ] - components: - - type: WorldTargetAction - icon: _Sunrise/FleshCult/Interface/Actions/fleshAcidSpit.png - itemIconStyle: NoItem - checkCanAccess: false - range: 200 - event: !type:FleshPudgeAcidSpitActionEvent - useDelay: 60 - -- type: entity - id: AbsorbBloodPool - name: flesh-cultist-absorb-blood-pool-name - description: flesh-cultist-absorb-blood-pool-desc - categories: [ HideSpawnMenu ] - components: - - type: InstantAction - icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistAbsorbBloodPool.png - event: !type:FleshPudgeAbsorbBloodPoolActionEvent - useDelay: 30 diff --git a/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_spider.yml b/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_spider.yml index 22ef509555..6060d6d3cf 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_spider.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/Actions/flesh_spider.yml @@ -7,4 +7,4 @@ - type: InstantAction icon: _Sunrise/FleshCult/Interface/Actions/flesh_web.png event: !type:SpiderWebActionEvent - useDelay: 30 + useDelay: 15 diff --git a/Resources/Prototypes/_Sunrise/FleshCult/Store/catalog.yml b/Resources/Prototypes/_Sunrise/FleshCult/Store/catalog.yml index 8c8c69b2b0..76918249e4 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/Store/catalog.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/Store/catalog.yml @@ -2,7 +2,10 @@ id: FleshCultistBlade name: flesh-cultist-blade-evolution-name description: flesh-cultist-blade-evolution-desc - productAction: FleshCultistBlade + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistBlade.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistBlade + raiseProductEventOnUser: true cost: StolenMutationPoint: 45 categories: @@ -90,7 +93,10 @@ id: FleshCultistClaw name: flesh-cultist-claw-evolution-name description: flesh-cultist-claw-evolution-desc - productAction: FleshCultistClaw + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistClaw.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistClaw + raiseProductEventOnUser: true cost: StolenMutationPoint: 10 categories: @@ -103,7 +109,10 @@ id: FleshCultistFist name: flesh-cultist-fist-evolution-name description: flesh-cultist-fist-evolution-desc - productAction: FleshCultistFist + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistFist.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistFist + raiseProductEventOnUser: true cost: StolenMutationPoint: 25 categories: @@ -116,7 +125,10 @@ id: FleshCultistSpikeGun name: flesh-cultist-spike-gun-evolution-name description: flesh-cultist-spike-gun-evolution-desc - productAction: FleshCultistSpikeGun + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistSpikeGun.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistSpikeGun + raiseProductEventOnUser: true cost: StolenMutationPoint: 35 categories: @@ -129,7 +141,10 @@ id: FleshCultistMediumArmor name: flesh-cultist-medium-armor-evolution-name description: flesh-cultist-medium-armor-evolution-desc - productAction: FleshCultistArmor + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistArmor.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistArmor + raiseProductEventOnUser: true cost: StolenMutationPoint: 25 categories: @@ -142,7 +157,10 @@ id: FleshCultistHeavyArmor name: flesh-cultist-heavy-armor-evolution-name description: flesh-cultist-heavy-armor-evolution-desc - productAction: FleshCultistHeavyArmor + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistHeavyArmor.png + raiseProductEventOnUser: true + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistHeavyArmor cost: StolenMutationPoint: 45 categories: @@ -155,7 +173,10 @@ id: FleshCultistSpiderlegs name: flesh-cultist-spider-legs-evolution-name description: flesh-cultist-spider-legs-evolution-desc - productAction: FleshCultistSpiderlegs + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistSpiderLegs.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistSpiderlegs + raiseProductEventOnUser: true cost: StolenMutationPoint: 20 categories: @@ -168,7 +189,10 @@ id: FleshCultistAdrenalin name: flesh-cultist-adrenaline-evolution-name description: flesh-cultist-adrenaline-evolution-desc - productAction: FleshCultistAdrenalin + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistAdrenalin.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistAdrenalin + raiseProductEventOnUser: true cost: StolenMutationPoint: 30 categories: @@ -181,7 +205,10 @@ id: FleshCultistCreateFleshHeart name: flesh-cultist-create-flesh-heart-evolution-name description: flesh-cultist-create-flesh-heart-evolution-desc - productAction: FleshCultistCreateFleshHeart + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistFleshHeart.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistCreateFleshHeart + raiseProductEventOnUser: true cost: StolenMutationPoint: 50 categories: @@ -194,7 +221,10 @@ id: FleshCultistThrowHugger name: flesh-cultist-throw-hugger-evolution-name description: flesh-cultist-throw-hugger-evolution-desc - productAction: FleshCultistThrowHugger + icon: _Sunrise/FleshCult/Interface/Actions/fleshThrowHugger.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistThrowHugger + raiseProductEventOnUser: true cost: StolenMutationPoint: 30 categories: @@ -207,7 +237,10 @@ id: FleshCultistAcidSpit name: flesh-cultist-acid-spit-evolution-name description: flesh-cultist-acid-spit-evolution-desc - productAction: FleshCultistAcidSpit + icon: _Sunrise/FleshCult/Interface/Actions/fleshAcidSpit.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistAcidSpit + raiseProductEventOnUser: true cost: StolenMutationPoint: 30 categories: @@ -215,3 +248,19 @@ conditions: - !type:ListingLimitedStockCondition stock: 1 + +- type: listing + id: FleshCultistAbsorbBloodPool + name: flesh-cultist-absorb-blood-pool-evolution-name + description: flesh-cultist-absorb-blood-pool-evolution-desc + icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistAbsorbBloodPool.png + productEvent: !type:FleshCultistUnlockAbilityEvent + prototype: FleshCultistAbsorbBloodPool + raiseProductEventOnUser: true + cost: + StolenMutationPoint: 10 + categories: + - FleshCultistActiveSkills + conditions: + - !type:ListingLimitedStockCondition + stock: 1 diff --git a/Resources/Prototypes/_Sunrise/FleshCult/body_mods.yml b/Resources/Prototypes/_Sunrise/FleshCult/body_mods.yml index f6b62cdab5..b3ba0cc82d 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/body_mods.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/body_mods.yml @@ -27,6 +27,10 @@ - LFoot - RLeg - LLeg + - type: FleshBodyMod + - type: Tag + tags: + - HidesLegs - type: entity categories: [ HideSpawnMenu ] @@ -45,13 +49,14 @@ - type: Armor modifiers: coefficients: - Blunt: 0.5 - Slash: 0.5 - Piercing: 0.2 + Blunt: 0.6 + Slash: 0.6 + Piercing: 0.6 Heat: 0.8 - type: ExplosionResistance damageCoefficient: 0.9 - type: GroupExamine + - type: FleshBodyMod - type: entity @@ -74,10 +79,10 @@ - type: Armor modifiers: coefficients: - Blunt: 0.6 - Slash: 0.6 - Piercing: 0.3 - Heat: 0.9 + Blunt: 0.4 + Slash: 0.4 + Piercing: 0.4 + Heat: 0.6 - type: ClothingSpeedModifier walkModifier: 0.8 sprintModifier: 0.8 @@ -88,6 +93,7 @@ deleteOnDrop: true - type: ToggleableClothing clothingPrototype: ClothingHeadHelmetHeavyArmorFlesh + - type: FleshBodyMod - type: entity diff --git a/Resources/Prototypes/_Sunrise/FleshCult/flesh_heart.yml b/Resources/Prototypes/_Sunrise/FleshCult/flesh_heart.yml index 394cd310d2..602f1028b4 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/flesh_heart.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/flesh_heart.yml @@ -47,8 +47,9 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: PointLight - radius: 10 - energy: 1 + radius: 25 + energy: 10 + enabled: false castShadows: true color: "#87031f" - type: FleshHeart @@ -58,9 +59,9 @@ types: Slash: 700 spawns: - MobFleshSpider: 0.20 + MobFleshSpider: 0.30 MobFleshPudge: 0.20 - MobFleshBall: 0.30 + MobFleshBall: 0.20 MobFleshBat: 0.30 - spawnMobsAmount: 3 + spawnMobsAmount: 5 spawnMobsFrequency: 180 diff --git a/Resources/Prototypes/_Sunrise/FleshCult/game_preset.yml b/Resources/Prototypes/_Sunrise/FleshCult/game_preset.yml index 9703cfabf5..aa4d914723 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/game_preset.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/game_preset.yml @@ -5,10 +5,12 @@ - fs name: flesh-cult-title description: flesh-cult-description - showInVote: false # Пока не закончим другие режимы. + showInVote: true hide: true rules: - FleshCult - LiteSubGamemodesRule - BasicStationEventScheduler + - MeteorSwarmScheduler + - SpaceTrafficControlEventScheduler - BasicRoundstartVariation diff --git a/Resources/Prototypes/_Sunrise/FleshCult/hands_mods.yml b/Resources/Prototypes/_Sunrise/FleshCult/hands_mods.yml index e210725f94..deaf0424d2 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/hands_mods.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/hands_mods.yml @@ -21,7 +21,7 @@ - Prying useSound: /Audio/Effects/gib2.ogg - type: Prying - speedModifier: 2.5 + speedModifier: 5 pryPowered: true force: true - type: MeleeWeapon diff --git a/Resources/Prototypes/_Sunrise/FleshCult/mobs.yml b/Resources/Prototypes/_Sunrise/FleshCult/mobs.yml index ccd83f889c..9d6546bbf6 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/mobs.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/mobs.yml @@ -135,6 +135,10 @@ collection: FootstepSpiderLegs params: volume: 10 + - type: Hands + showInHands: false + disableExplosionRecursion: true + canBeStripped: false - type: entity parent: BaseMobFleshCult @@ -156,28 +160,18 @@ - type: Prying pryPowered: true force: true - speedModifier: 1.5 + speedModifier: 7 useSound: path: /Audio/Items/crowbar.ogg - type: Butcherable spawned: - id: FoodMeat amount: 5 - - type: FleshPudge - bloodAbsorbSound: - path: /Audio/Effects/Fluids/splat.ogg - healBloodAbsorbReagents: - reagents: - - data: null - ReagentId: Carol - Quantity: 1 - bulletAcidSpawnId: BulletSplashAcid - faceHuggerMobSpawnId: MobFleshHugger - soundThrowWorm: !type:SoundPathSpecifier - path: /Audio/_Sunrise/FleshCult/throw_worm.ogg - actionAbsorbBloodPool: AbsorbBloodPool - actionAcidSpit: FleshAcidSpit - actionThrowWorm: FleshThrowWorm + - type: FleshAbilities + startingActions: + - FleshCultistThrowHugger + - FleshCultistAcidSpit + - FleshCultistAbsorbBloodPool - type: Vocal sounds: Unsexed: FleshPudgeEmote @@ -240,7 +234,7 @@ fix1: shape: !type:PhysShapeCircle - radius: 0.40 + radius: 0.35 density: 800 mask: - MobMask @@ -248,6 +242,10 @@ - MobLayer - type: Puller needsHands: false + - type: Hands + showInHands: false + disableExplosionRecursion: true + canBeStripped: false - type: entity diff --git a/Resources/Prototypes/_Sunrise/FleshCult/objectives.yml b/Resources/Prototypes/_Sunrise/FleshCult/objectives.yml index ecae730ee8..ab979f9be2 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/objectives.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/objectives.yml @@ -8,8 +8,8 @@ issuer: flesh-cult - type: RoleRequirement roles: - components: - - FleshCultistRole + mindRoles: + - FleshCultistRole - type: entity categories: [ HideSpawnMenu ] diff --git a/Resources/Prototypes/_Sunrise/FleshCult/roundstart.yml b/Resources/Prototypes/_Sunrise/FleshCult/roundstart.yml index db28307c4d..c72496acf9 100644 --- a/Resources/Prototypes/_Sunrise/FleshCult/roundstart.yml +++ b/Resources/Prototypes/_Sunrise/FleshCult/roundstart.yml @@ -23,6 +23,19 @@ - type: NpcFactionMember factions: - FleshHuman + whitelist: + species: + - Human + - Reptilian + - Dwarf + - Vulpkanin + - Felinid + - Moth + - Arachnid + - Swine + - Demon + - Vox + - Tajaran blacklist: components: - AntagImmune