Глобальная оптимизация и фиксы культа плоти
This commit is contained in:
parent
486b666199
commit
bc46f14574
50 changed files with 2439 additions and 2968 deletions
|
|
@ -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!;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
using Content.Server.Flesh;
|
||||
|
||||
namespace Content.Client._Sunrise.FleshCult;
|
||||
|
||||
public sealed class FleshHuggerSystem: SharedFleshHuggerSystem
|
||||
{
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,18 @@ public sealed partial class AdminVerbSystem
|
|||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string DefaultThiefRule = "Thief";
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string DefaultVampireRule = "Vampire";
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string DefaultChangelingRule = "Changeling";
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string DefaultFleshCultRule = "FleshCult";
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string DefaultAssaultOpsRule = "AssaultOps";
|
||||
|
||||
[ValidatePrototypeId<StartingGearPrototype>]
|
||||
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<ChangelingRuleComponent>(targetPlayer, "Changeling");
|
||||
_antag.ForceMakeAntag<ChangelingRuleComponent>(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<VampireRuleComponent>(targetPlayer, "Vampire");
|
||||
_antag.ForceMakeAntag<VampireRuleComponent>(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<AssaultOpsRuleComponent>(targetPlayer, "AssaultOps");
|
||||
_antag.ForceMakeAntag<AssaultOpsRuleComponent>(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<FleshCultRuleComponent>(targetPlayer, "FleshCult");
|
||||
_antag.ForceMakeAntag<FleshCultRuleComponent>(targetPlayer, DefaultFleshCultRule);
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Message = Loc.GetString("admin-verb-make-flesh-cultist"),
|
||||
};
|
||||
// На время пока не будут закончены все новые режимы.
|
||||
//args.Verbs.Add(fleshCultist);
|
||||
args.Verbs.Add(fleshCultist);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
81
Content.Server/_Sunrise/FleshCult/FleshAbilitiesComponent.cs
Normal file
81
Content.Server/_Sunrise/FleshCult/FleshAbilitiesComponent.cs
Normal file
|
|
@ -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<EntityPrototype>))]
|
||||
public List<string> StartingActions = new();
|
||||
|
||||
[DataField]
|
||||
public List<EntityUid> Actions = new();
|
||||
|
||||
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ActionFleshCultistDevourId = "FleshCultistDevour";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("bloodWhitelist")]
|
||||
public List<string> 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<EntityPrototype>))]
|
||||
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<EntityPrototype>)),
|
||||
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<EntityPrototype>))]
|
||||
public string HuggerMobSpawnId = "MobFleshHugger";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Content.Server._Sunrise.FleshCult
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class FleshBodyModComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
569
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs
Normal file
569
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Abilities.cs
Normal file
|
|
@ -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<FleshAbilitiesComponent, FleshCultistHandTransformEvent>(OnHandTransformEvent);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistBodyTransformEvent>(OnBodyModificationAction);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistAdrenalinActionEvent>(OnAdrenalinActionEvent);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistCreateFleshHeartActionEvent>(OnCreateFleshHeartActionEvent);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistThrowHuggerActionEvent>(OnThrowHugger);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistAcidSpitActionEvent>(OnAcidSpit);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistAbsorbBloodPoolActionEvent>(AbsormBloodPool);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistDevourActionEvent>(OnDevourAction);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistDevourDoAfterEvent>(OnDevourDoAfter);
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, FleshCultistUnlockAbilityEvent>(OnUnlockAbility);
|
||||
|
||||
SubscribeLocalEvent<FleshAbilitiesComponent, ComponentStartup>(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<MobStateComponent>(target, out var targetState))
|
||||
return;
|
||||
if (!TryComp<BloodstreamComponent>(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<FixturesComponent>(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<FleshCultistComponent>(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<BloodstreamComponent>(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<HumanoidAppearanceComponent>(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<BodyPartComponent>(ent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_containerSystem.Remove(ent, cont, force: true);
|
||||
Transform(ent).Coordinates = coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SUNRISE-TODO: Убрать конечности хирургией а тело заменить на скелета
|
||||
if (TryComp<BodyComponent>(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<HumanoidSpeciesBaseSpritesPrototype>("MobSkeletonSprites");
|
||||
foreach (var (key, id) in skeletonSprites.Sprites)
|
||||
{
|
||||
if (key != HumanoidVisualLayers.Head)
|
||||
{
|
||||
_sharedHuApp.SetBaseLayerId(args.Args.Target.Value, key, id, humanoid: HuAppComponent);
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<FixturesComponent>(args.Args.Target, out var fixturesComponent))
|
||||
{
|
||||
_physics.SetDensity(args.Args.Target.Value, "fix1", fixturesComponent.Fixtures["fix1"], 50);
|
||||
}
|
||||
|
||||
if (TryComp<AppearanceComponent>(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<BloodstreamComponent>(args.Args.Target.Value);
|
||||
|
||||
EnsureComp<UnrevivableComponent>(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<FleshCultistComponent>(uid, out var fleshCultistComponent))
|
||||
{
|
||||
fleshCultistComponent.Hunger += saturation;
|
||||
_store.TryAddCurrency(new Dictionary<string, FixedPoint2>
|
||||
{ {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<PuddleComponent>(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<CuffableComponent>(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<FleshHandModComponent>(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<CuffableComponent>(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<CuffableComponent>(uid))
|
||||
{
|
||||
EntityManager.RemoveComponent<CuffableComponent>(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<FleshBodyModComponent>(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<FleshBodyModComponent>(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<MapGridComponent>(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<TileRef>(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<MobStateComponent>(entity) && entity != uid || // Is it a mob?
|
||||
Resolve(entity, ref physics, false) && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0 ||
|
||||
HasComp<ConstructionComponent>(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);
|
||||
}
|
||||
|
||||
}
|
||||
432
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs
Normal file
432
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Cultist.cs
Normal file
|
|
@ -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<CollectiveMindPrototype>]
|
||||
private const string FleshCollectiveMindProto = "FleshCult";
|
||||
|
||||
[ValidatePrototypeId<TagPrototype>]
|
||||
private const string FleshTagProto = "Flesh";
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string DefaultFleshCultRule = "FleshCult";
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string CreateFleshHeartObjective = "CreateFleshHeartObjective";
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
private const string FleshCultSurviveObjective = "FleshCultSurviveObjective";
|
||||
|
||||
private void InitializeCultist()
|
||||
{
|
||||
SubscribeLocalEvent<FleshCultistComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<FleshCultistComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistInsulatedImmunityMutationEvent>(OnInsulatedImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistPressureImmunityMutationEvent>(OnPressureImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistFlashImmunityMutationEvent>(OnFlashImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistRespiratorImmunityMutationEvent>(OnRespiratorImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistColdTempImmunityMutationEvent>(OnColdTempImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, IsEquippingAttemptEvent>(OnBeingEquippedAttempt);
|
||||
SubscribeLocalEvent<FleshCultistComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistShopActionEvent>(OnShop);
|
||||
}
|
||||
|
||||
private void OnShop(EntityUid uid, FleshCultistComponent component, FleshCultistShopActionEvent args)
|
||||
{
|
||||
if (!TryComp<StoreComponent>(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<CuffableComponent>(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<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 (HasComp<FleshBodyModComponent>(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<FleshBodyModComponent>(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<FleshBodyModComponent>(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<FleshAbilitiesComponent>(uid);
|
||||
var devourAction = _action.AddAction(uid, fleshAbilities.ActionFleshCultistDevourId, component: actionsComponent);
|
||||
if (devourAction != null)
|
||||
fleshAbilities.Actions.Add(devourAction.Value);
|
||||
}
|
||||
|
||||
var storeComp = EnsureComp<StoreComponent>(uid);
|
||||
|
||||
var collectiveMindComponent = EnsureComp<CollectiveMindComponent>(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<IgnoreFleshSpiderWebComponent>(uid);
|
||||
|
||||
if (HasComp<HungerComponent>(uid))
|
||||
RemComp<HungerComponent>(uid);
|
||||
|
||||
if (HasComp<ThirstComponent>(uid))
|
||||
RemComp<ThirstComponent>(uid);
|
||||
|
||||
_tagSystem.AddTag(uid, FleshTagProto);
|
||||
|
||||
if (TryComp<HumanoidAppearanceComponent>(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<IgnoreFleshSpiderWebComponent>(uid);
|
||||
RemCompDeferred<InsulatedComponent>(uid);
|
||||
RemCompDeferred<FlashImmunityComponent>(uid);
|
||||
RemCompDeferred<RespiratorImmunityComponent>(uid);
|
||||
RemCompDeferred<PressureImmunityComponent>(uid);
|
||||
RemCompDeferred<FlashImmunityComponent>(uid);
|
||||
|
||||
if (TryComp(uid, out CollectiveMindComponent? collectiveMind))
|
||||
{
|
||||
if (collectiveMind.Minds.Contains(FleshCollectiveMindProto))
|
||||
collectiveMind.Minds.Remove(FleshCollectiveMindProto);
|
||||
}
|
||||
|
||||
EnsureComp<HungerComponent>(uid);
|
||||
EnsureComp<ThirstComponent>(uid);
|
||||
|
||||
_alerts.ClearAlert(uid, component.MutationPointAlert);
|
||||
|
||||
_tagSystem.RemoveTag(uid, FleshTagProto);
|
||||
|
||||
if (_mindSystem.TryGetMind(uid, out var mindId, out var mind))
|
||||
{
|
||||
_roles.MindRemoveRole<FleshCultistRoleComponent>((mindId, mind));
|
||||
|
||||
var indexesToRemove = new List<int>();
|
||||
|
||||
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<InsulatedComponent>(uid);
|
||||
}
|
||||
|
||||
|
||||
private void OnPressureImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistPressureImmunityMutationEvent args)
|
||||
{
|
||||
EnsureComp<PressureImmunityComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnFlashImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistFlashImmunityMutationEvent args)
|
||||
{
|
||||
EnsureComp<FlashImmunityComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnRespiratorImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistRespiratorImmunityMutationEvent args)
|
||||
{
|
||||
EnsureComp<RespiratorImmunityComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnColdTempImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistColdTempImmunityMutationEvent args)
|
||||
{
|
||||
if (TryComp<TemperatureComponent>(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<StoreComponent>(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<BodyPartComponent>(ent))
|
||||
continue;
|
||||
if (HasComp<UnremoveableComponent>(ent))
|
||||
continue;
|
||||
_containerSystem.Remove(ent, cont, force: true);
|
||||
Transform(ent).Coordinates = coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<BloodstreamComponent>(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<DnaComponent>(uid, out var dna))
|
||||
{
|
||||
var comp = EnsureComp<ForensicsComponent>(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<FleshCultistComponent>())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
425
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Heart.cs
Normal file
425
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Heart.cs
Normal file
|
|
@ -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<FleshHeartComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<FleshHeartComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<FleshHeartComponent, DestructionEventArgs>(OnDestruction);
|
||||
SubscribeLocalEvent<FleshHeartComponent, DragDropTargetEvent>(HandleDragDropOn);
|
||||
SubscribeLocalEvent<FleshHeartComponent, FleshHeartDragFinished>(OnDragFinished);
|
||||
SubscribeLocalEvent<FleshHeartComponent, ComponentInit>(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<SpreaderFleshComponent>();
|
||||
while (fleshTilesQuery.MoveNext(out var ent, out var comp))
|
||||
{
|
||||
if (comp.Source != uid)
|
||||
continue;
|
||||
if (!TryComp<TagComponent>(ent, out var tagComponent))
|
||||
continue;
|
||||
if (_tagSystem.HasAllTags(tagComponent, "Wall", "Flesh"))
|
||||
_damageableSystem.TryChangeDamage(ent, component.DamageMobsIfHeartDestruct);
|
||||
else
|
||||
QueueDel(ent);
|
||||
|
||||
}
|
||||
var fleshWalls = new List<EntityUid>();
|
||||
var fleshWallsQuery = EntityQueryEnumerator<TagComponent>();
|
||||
while (fleshWallsQuery.MoveNext(out var ent, out var comp))
|
||||
{
|
||||
if (!TryComp<TagComponent>(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<FleshCultRuleComponent>().FirstOrDefault();
|
||||
if (fleshCultRule == null)
|
||||
return;
|
||||
|
||||
var fleshHeartQuery = EntityQueryEnumerator<FleshHeartComponent, TransformComponent>();
|
||||
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<FixturesComponent>(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<Container>(uid, "bodyContainer");
|
||||
}
|
||||
|
||||
private void OnDragFinished(EntityUid uid, FleshHeartComponent component, FleshHeartDragFinished args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled || args.Args.Target == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<FixturesComponent>(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<BodyPartComponent>(ent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_containerSystem.Remove(ent, cont, force: true);
|
||||
Transform(ent).Coordinates = xform.Coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SUNRISE-TODO: Убрать конечности хирургией а тело заменить на скелета
|
||||
if (TryComp<HumanoidAppearanceComponent>(args.Args.Target.Value, out var HuAppComponent))
|
||||
{
|
||||
if (TryComp<BloodstreamComponent>(args.Args.Target.Value, out var bloodstreamComponent))
|
||||
_bloodstreamSystem.TryModifyBloodLevel(args.Args.Target.Value, -300, bloodstreamComponent);
|
||||
|
||||
if (TryComp<BodyComponent>(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<HumanoidSpeciesBaseSpritesPrototype>("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<AppearanceComponent>(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<UnrevivableComponent>(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<MobStateComponent>(dragged, out var stateComponent))
|
||||
return false;
|
||||
|
||||
if (stateComponent.CurrentState != MobState.Dead)
|
||||
return false;
|
||||
|
||||
if (!Transform(uid).Anchored)
|
||||
return false;
|
||||
|
||||
if (!TryComp<HumanoidAppearanceComponent>(dragged, out var humanoidAppearance))
|
||||
return false;
|
||||
|
||||
if (!_speciesWhitelist.Contains(humanoidAppearance.Species))
|
||||
return false;
|
||||
|
||||
return !TryComp<MindContainerComponent>(dragged, out var mindComp) || true;
|
||||
}
|
||||
|
||||
private void SpawnFleshFloorOnOpenTiles(EntityUid fleshHeart, FleshHeartComponent component, TransformComponent xform, float radius)
|
||||
{
|
||||
if (!TryComp<MapGridComponent>(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<SpreaderFleshComponent>(fleshTile);
|
||||
spreaderFleshComponent.Source = fleshHeart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnMonstersOnOpenTiles(FleshHeartComponent component, TransformComponent xform, int amount, float radius)
|
||||
{
|
||||
if (!TryComp<MapGridComponent>(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<PhysicsComponent>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
257
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Hugger.cs
Normal file
257
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Hugger.cs
Normal file
|
|
@ -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<FleshHuggerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, ThrowDoHitEvent>(OnWormDoHit);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, GotEquippedHandEvent>(OnGotEquippedHand);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, FleshHuggerJumpActionEvent>(OnJump);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, FleshHuggerGetOffFromFaceActionEvent>(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<FleshCultistComponent>(args.Target))
|
||||
return;
|
||||
if (!HasComp<HumanoidAppearanceComponent>(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<IngestionBlockerComponent>(headItem))
|
||||
return;
|
||||
|
||||
_inventory.TryGetSlotEntity(args.Target, "mask", out var maskItem);
|
||||
if (HasComp<IdentityBlockerComponent>(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<PacifiedComponent>(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<TemporaryBlindnessComponent>(args.Equipee);
|
||||
EntityManager.EnsureComponent<PacifiedComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnGotEquippedHand(EntityUid uid, FleshHuggerComponent component, GotEquippedHandEvent args)
|
||||
{
|
||||
if (HasComp<FleshCultistComponent>(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<PacifiedComponent>(uid))
|
||||
EntityManager.RemoveComponent<PacifiedComponent>(uid);
|
||||
if (HasComp<TemporaryBlindnessComponent>(component.EquipedOn))
|
||||
EntityManager.RemoveComponent<TemporaryBlindnessComponent>(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<HumanoidAppearanceComponent>(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<IngestionBlockerComponent>(headItem))
|
||||
return;
|
||||
|
||||
_inventory.TryGetSlotEntity(entity, "mask", out var maskItem);
|
||||
if (HasComp<IdentityBlockerComponent>(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<PacifiedComponent>(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<FleshHuggerComponent>())
|
||||
{
|
||||
comp.Accumulator += frameTime;
|
||||
|
||||
if (comp.Accumulator <= comp.DamageFrequency)
|
||||
continue;
|
||||
|
||||
comp.Accumulator = 0;
|
||||
|
||||
if (comp.EquipedOn is not { Valid: true } targetId)
|
||||
continue;
|
||||
if (HasComp<FleshCultistComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Mob.cs
Normal file
29
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Mob.cs
Normal file
|
|
@ -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<FleshMobComponent, MobStateChangedEvent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
120
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Virus.cs
Normal file
120
Content.Server/_Sunrise/FleshCult/FleshCultSystem.Virus.cs
Normal file
|
|
@ -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<PendingFleshCultistComponent, MapInitEvent>(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<PendingFleshCultistComponent>();
|
||||
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<MindContainerComponent>(uid) || !TryComp<ActorComponent>(uid, out var targetActor))
|
||||
return;
|
||||
|
||||
var targetPlayer = targetActor.PlayerSession;
|
||||
|
||||
if (HasComp<MindShieldComponent>(uid))
|
||||
{
|
||||
// SUNRISE-TODO: Сделать это внутри системы майншилда
|
||||
_popup.PopupEntity("Активация самоуничтожения импланта защиты разума", uid, PopupType.LargeCaution);
|
||||
_body.GibBody(uid, true);
|
||||
_explosionSystem.QueueExplosion(uid, "Default", 50, 5, 30, canCreateVacuum: false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!TryComp<HumanoidAppearanceComponent>(uid, out var humanoidAppearance))
|
||||
break;
|
||||
|
||||
if (_speciesWhitelist.Contains(humanoidAppearance.Species))
|
||||
{
|
||||
RemCompDeferred<PendingFleshCultistComponent>(uid);
|
||||
break;
|
||||
}
|
||||
|
||||
_antag.ForceMakeAntag<FleshCultRuleComponent>(targetPlayer, DefaultFleshCultRule);
|
||||
|
||||
comp.CurrentStage = PendingFleshCultistStage.Final;
|
||||
RemCompDeferred<PendingFleshCultistComponent>(uid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PendingFleshCultistStage GetNextStage(PendingFleshCultistStage currentStage)
|
||||
{
|
||||
return currentStage switch
|
||||
{
|
||||
PendingFleshCultistStage.First => PendingFleshCultistStage.Second,
|
||||
PendingFleshCultistStage.Second => PendingFleshCultistStage.Third,
|
||||
PendingFleshCultistStage.Third => PendingFleshCultistStage.Final,
|
||||
_ => currentStage,
|
||||
};
|
||||
}
|
||||
}
|
||||
118
Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs
Normal file
118
Content.Server/_Sunrise/FleshCult/FleshCultSystem.cs
Normal file
|
|
@ -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<string> _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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FleshCultistComponent, FleshCultistBladeActionEvent>(OnBladeActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistClawActionEvent>(OnClawActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistFistActionEvent>(OnFistActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistSpikeHandGunActionEvent>(OnSpikeHandGunActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistArmorActionEvent>(OnArmorActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistHeavyArmorActionEvent>(OnHeavyArmorActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistSpiderLegsActionEvent>(OnSpiderLegsActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistAdrenalinActionEvent>(OnAdrenalinActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistCreateFleshHeartActionEvent>(OnCreateFleshHeartActionEvent);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistThrowHuggerActionEvent>(OnThrowHugger);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistAcidSpitActionEvent>(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<CuffableComponent>(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<Hand>(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<CuffableComponent>(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<CuffableComponent>(uid))
|
||||
{
|
||||
EntityManager.RemoveComponent<CuffableComponent>(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<CuffableComponent>(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<Hand>(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<CuffableComponent>(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<CuffableComponent>(uid))
|
||||
EntityManager.RemoveComponent<CuffableComponent>(uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueDel(claw);
|
||||
}
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
private void OnFistActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistFistActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
if (TryComp<CuffableComponent>(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<Hand>(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<CuffableComponent>(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<CuffableComponent>(uid))
|
||||
EntityManager.RemoveComponent<CuffableComponent>(uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueDel(fist);
|
||||
}
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnSpikeHandGunActionEvent(EntityUid uid, FleshCultistComponent component, FleshCultistSpikeHandGunActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
if (TryComp<CuffableComponent>(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<Hand>(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<CuffableComponent>(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<CuffableComponent>(uid))
|
||||
RemComp<CuffableComponent>(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<MapGridComponent>(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<TileRef>(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<MobStateComponent>(entity) && entity != uid || // Is it a mob?
|
||||
Resolve(entity, ref physics, false) && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0 ||
|
||||
HasComp<ConstructionComponent>(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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<FleshCultistComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistShopActionEvent>(OnShop);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistInsulatedImmunityMutationEvent>(OnInsulatedImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistPressureImmunityMutationEvent>(OnPressureImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistFlashImmunityMutationEvent>(OnFlashImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistRespiratorImmunityMutationEvent>(OnRespiratorImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistColdTempImmunityMutationEvent>(OnColdTempImmunityMutation);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistDevourActionEvent>(OnDevourAction);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistDevourDoAfterEvent>(OnDevourDoAfter);
|
||||
SubscribeLocalEvent<FleshCultistComponent, IsEquippingAttemptEvent>(OnBeingEquippedAttempt);
|
||||
SubscribeLocalEvent<FleshCultistComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<FleshCultistComponent, FleshCultistAbsorbBloodPoolActionEvent>(OnAbsormBloodPoolActionEvent);
|
||||
|
||||
SubscribeLocalEvent<PendingFleshCultistComponent, MapInitEvent>(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<CuffableComponent>(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<StoreComponent>(uid);
|
||||
|
||||
var collectiveMindComponent = EnsureComp<CollectiveMindComponent>(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<IgnoreFleshSpiderWebComponent>(uid);
|
||||
|
||||
if (HasComp<HungerComponent>(uid))
|
||||
RemComp<HungerComponent>(uid);
|
||||
|
||||
if (HasComp<ThirstComponent>(uid))
|
||||
RemComp<ThirstComponent>(uid);
|
||||
|
||||
_tagSystem.AddTag(uid, "Flesh");
|
||||
|
||||
if (TryComp<HumanoidAppearanceComponent>(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<InsulatedComponent>(uid);
|
||||
}
|
||||
|
||||
|
||||
private void OnPressureImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistPressureImmunityMutationEvent args)
|
||||
{
|
||||
EnsureComp<PressureImmunityComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnFlashImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistFlashImmunityMutationEvent args)
|
||||
{
|
||||
EnsureComp<FlashImmunityComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnRespiratorImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistRespiratorImmunityMutationEvent args)
|
||||
{
|
||||
EnsureComp<RespiratorImmunityComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnColdTempImmunityMutation(EntityUid uid, FleshCultistComponent component,
|
||||
FleshCultistColdTempImmunityMutationEvent args)
|
||||
{
|
||||
if (TryComp<TemperatureComponent>(uid, out var tempComponent))
|
||||
{
|
||||
tempComponent.ColdDamageThreshold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShop(EntityUid uid, FleshCultistComponent component, FleshCultistShopActionEvent args)
|
||||
{
|
||||
if (!TryComp<StoreComponent>(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<StoreComponent>(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<MobStateComponent>(target, out var targetState))
|
||||
return;
|
||||
if (!TryComp<BloodstreamComponent>(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<FixturesComponent>(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<BloodstreamComponent>(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<HumanoidAppearanceComponent>(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<BodyPartComponent>(ent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_containerSystem.Remove(ent, cont, force: true);
|
||||
Transform(ent).Coordinates = coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<BodyComponent>(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<HumanoidSpeciesBaseSpritesPrototype>("MobSkeletonSprites");
|
||||
foreach (var (key, id) in skeletonSprites.Sprites)
|
||||
{
|
||||
if (key != HumanoidVisualLayers.Head)
|
||||
{
|
||||
_sharedHuApp.SetBaseLayerId(args.Args.Target.Value, key, id, humanoid: HuAppComponent);
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<FixturesComponent>(args.Args.Target, out var fixturesComponent))
|
||||
{
|
||||
_physics.SetDensity(args.Args.Target.Value, "fix1", fixturesComponent.Fixtures["fix1"], 50);
|
||||
}
|
||||
|
||||
if (TryComp<AppearanceComponent>(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<BloodstreamComponent>(args.Args.Target.Value);
|
||||
|
||||
EnsureComp<UnrevivableComponent>(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<string, FixedPoint2>
|
||||
{ {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<BodyPartComponent>(ent))
|
||||
continue;
|
||||
if (HasComp<UnremoveableComponent>(ent))
|
||||
continue;
|
||||
_containerSystem.Remove(ent, cont, force: true);
|
||||
Transform(ent).Coordinates = coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<BloodstreamComponent>(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<DnaComponent>(uid, out var dna))
|
||||
{
|
||||
var comp = EnsureComp<ForensicsComponent>(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<PendingFleshCultistComponent>();
|
||||
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<MindContainerComponent>(uid, out var targetMindComp))
|
||||
return;
|
||||
|
||||
if (HasComp<MindShieldComponent>(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<FleshCultistComponent>())
|
||||
{
|
||||
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<PuddleComponent>(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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<FleshHeartComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<FleshHeartComponent, DestructionEventArgs>(OnDestruction);
|
||||
SubscribeLocalEvent<FleshHeartComponent, DragDropTargetEvent>(HandleDragDropOn);
|
||||
SubscribeLocalEvent<FleshHeartComponent, FleshHeartDragFinished>(OnDragFinished);
|
||||
SubscribeLocalEvent<FleshHeartComponent, ComponentInit>(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<SpreaderFleshComponent>();
|
||||
while (fleshTilesQuery.MoveNext(out var ent, out var comp))
|
||||
{
|
||||
if (comp.Source != uid)
|
||||
continue;
|
||||
if (!TryComp<TagComponent>(ent, out var tagComponent))
|
||||
continue;
|
||||
if (_tagSystem.HasAllTags(tagComponent, "Wall", "Flesh"))
|
||||
_damageableSystem.TryChangeDamage(ent, component.DamageMobsIfHeartDestruct);
|
||||
else
|
||||
QueueDel(ent);
|
||||
|
||||
}
|
||||
var fleshWalls = new List<EntityUid>();
|
||||
var fleshWallsQuery = EntityQueryEnumerator<TagComponent>();
|
||||
while (fleshWallsQuery.MoveNext(out var ent, out var comp))
|
||||
{
|
||||
if (!TryComp<TagComponent>(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<FleshHeartComponent, TransformComponent>();
|
||||
while (fleshHeartQuery.MoveNext(out var ent, out var comp, out var xform))
|
||||
{
|
||||
var fleshCultRule = EntityQuery<FleshCultRuleComponent>().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<FixturesComponent>(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<Container>(uid, "bodyContainer");
|
||||
}
|
||||
|
||||
private void OnDragFinished(EntityUid uid, FleshHeartComponent component, FleshHeartDragFinished args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled || args.Args.Target == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<FixturesComponent>(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<BodyPartComponent>(ent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_containerSystem.Remove(ent, cont, force: true);
|
||||
Transform(ent).Coordinates = xform.Coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<HumanoidAppearanceComponent>(args.Args.Target.Value, out var HuAppComponent))
|
||||
{
|
||||
if (TryComp<BodyComponent>(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<HumanoidSpeciesBaseSpritesPrototype>("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<AppearanceComponent>(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<UnrevivableComponent>(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<MobStateComponent>(dragged, out var stateComponent))
|
||||
return false;
|
||||
|
||||
if (stateComponent.CurrentState != MobState.Dead)
|
||||
return false;
|
||||
|
||||
if (!Transform(uid).Anchored)
|
||||
return false;
|
||||
|
||||
if (!TryComp<HumanoidAppearanceComponent>(dragged, out var humanoidAppearance))
|
||||
return false;
|
||||
|
||||
if (!(component.SpeciesWhitelist.Contains(humanoidAppearance.Species)))
|
||||
return false;
|
||||
|
||||
return !TryComp<MindContainerComponent>(dragged, out var mindComp) || true;
|
||||
}
|
||||
|
||||
private void SpawnFleshFloorOnOpenTiles(EntityUid fleshHeart, FleshHeartComponent component, TransformComponent xform, float radius)
|
||||
{
|
||||
if (!TryComp<MapGridComponent>(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<SpreaderFleshComponent>(fleshTile);
|
||||
spreaderFleshComponent.Source = fleshHeart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnMonstersOnOpenTiles(FleshHeartComponent component, TransformComponent xform, int amount, float radius)
|
||||
{
|
||||
if (!TryComp<MapGridComponent>(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<PhysicsComponent>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FleshHuggerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, ThrowDoHitEvent>(OnWormDoHit);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, GotEquippedHandEvent>(OnGotEquippedHand);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, FleshHuggerJumpActionEvent>(OnJump);
|
||||
SubscribeLocalEvent<FleshHuggerComponent, FleshHuggerGetOffFromFaceActionEvent>(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<FleshCultistComponent>(args.Target))
|
||||
return;
|
||||
if (!HasComp<HumanoidAppearanceComponent>(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<IngestionBlockerComponent>(headItem))
|
||||
return;
|
||||
|
||||
_inventory.TryGetSlotEntity(args.Target, "mask", out var maskItem);
|
||||
if (HasComp<IdentityBlockerComponent>(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<PacifiedComponent>(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<TemporaryBlindnessComponent>(args.Equipee);
|
||||
EntityManager.EnsureComponent<PacifiedComponent>(uid);
|
||||
}
|
||||
|
||||
private void OnGotEquippedHand(EntityUid uid, FleshHuggerComponent component, GotEquippedHandEvent args)
|
||||
{
|
||||
if (HasComp<_Sunrise.FleshCult.FleshPudgeComponent>(args.User))
|
||||
return;
|
||||
if (HasComp<FleshCultistComponent>(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<PacifiedComponent>(uid))
|
||||
EntityManager.RemoveComponent<PacifiedComponent>(uid);
|
||||
if (HasComp<TemporaryBlindnessComponent>(component.EquipedOn))
|
||||
EntityManager.RemoveComponent<TemporaryBlindnessComponent>(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<HumanoidAppearanceComponent>(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<IngestionBlockerComponent>(headItem))
|
||||
return;
|
||||
|
||||
_inventory.TryGetSlotEntity(entity, "mask", out var maskItem);
|
||||
if (HasComp<IdentityBlockerComponent>(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<PacifiedComponent>(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<FleshHuggerComponent>())
|
||||
{
|
||||
comp.Accumulator += frameTime;
|
||||
|
||||
if (comp.Accumulator <= comp.DamageFrequency)
|
||||
continue;
|
||||
|
||||
comp.Accumulator = 0;
|
||||
|
||||
if (comp.EquipedOn is not { Valid: true } targetId)
|
||||
continue;
|
||||
if (HasComp<FleshCultistComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FleshMobComponent, MobStateChangedEvent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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<EntityPrototype>))]
|
||||
public string ActionThrowWormId = "FleshThrowWorm";
|
||||
|
||||
[DataField("actionAcidSpit", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ActionAcidSpitId = "FleshAcidSpit";
|
||||
|
||||
[DataField("actionAbsorbBloodPool", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
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<EntityPrototype>))]
|
||||
public string FaceHuggerMobSpawnId = "MobFleshHugger";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("bulletAcidSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
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<string> BloodWhitelist = new()
|
||||
{
|
||||
"Blood",
|
||||
"CopperBlood",
|
||||
"InsectBlood",
|
||||
"AmmoniaBlood",
|
||||
"ZombieBlood"
|
||||
};
|
||||
|
||||
[DataField("bloodAbsorbSound")]
|
||||
public SoundSpecifier BloodAbsorbSound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg");
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FleshPudgeComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<FleshPudgeComponent, FleshPudgeThrowFaceHuggerActionEvent>(OnThrowFaceHugger);
|
||||
SubscribeLocalEvent<FleshPudgeComponent, FleshPudgeAbsorbBloodPoolActionEvent>(OnAbsorbBloodPoolActionEvent);
|
||||
SubscribeLocalEvent<FleshPudgeComponent, FleshPudgeAcidSpitActionEvent>(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<PuddleComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,6 @@ public sealed partial class FleshCultRuleComponent : Component
|
|||
public SoundSpecifier AddedSound = new SoundPathSpecifier(
|
||||
"/Audio/_Sunrise/FleshCult/flesh_culstis_greeting.ogg");
|
||||
|
||||
public Dictionary<string, EntityUid> Cultists = new();
|
||||
|
||||
[DataField("fleshCultistPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public string FleshCultistPrototypeId = "FleshCultist";
|
||||
|
||||
|
|
@ -27,21 +25,15 @@ public sealed partial class FleshCultRuleComponent : Component
|
|||
[DataField("faction", customTypeSerializer: typeof(PrototypeIdSerializer<NpcFactionPrototype>), required: true)]
|
||||
public string Faction = default!;
|
||||
|
||||
public List<EntityUid> Cultists = new();
|
||||
public int TotalCultists => Cultists.Count;
|
||||
|
||||
public readonly List<string> CultistsNames = new();
|
||||
|
||||
public WinTypes WinType = WinTypes.Fail;
|
||||
|
||||
public bool FleshHeartActive = false;
|
||||
|
||||
public Dictionary<EntityUid, FleshHeartStatus> FleshHearts = new();
|
||||
|
||||
public EntityUid? TargetStation;
|
||||
|
||||
[DataField]
|
||||
public List<string> StarterItems = new() { "SyringeCarolNT", "SyringeCarolNT", "SyringeCarolNT" };
|
||||
|
||||
public List<string> 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<ICommonSession, HumanoidCharacterProfile> StartCandidates = new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<FleshCultRuleComponent>
|
|||
[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<AntagPrototype>]
|
||||
private const string LeaderAntagProto = "FleshCultistLeader";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<FleshCultRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
|
||||
|
||||
SubscribeLocalEvent<FleshHeartSystem.FleshHeartActivateEvent>(OnFleshHeartActivate);
|
||||
SubscribeLocalEvent<FleshHeartSystem.FleshHeartDestructionEvent>(OnFleshHeartDestruction);
|
||||
SubscribeLocalEvent<FleshHeartSystem.FleshHeartFinalEvent>(OnFleshHeartFinal);
|
||||
|
||||
SubscribeLocalEvent<FleshCultRuleComponent, ObjectivesTextPrependEvent>(OnObjectivesTextPrepend);
|
||||
SubscribeLocalEvent<FleshCultRuleComponent, AntagSelectionCompleteEvent>(OnAfterAntagSelectionComplete);
|
||||
SubscribeLocalEvent<FleshCultSystem.FleshHeartStatusChangeEvent>(OnFleshHeartStatusChange);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid,
|
||||
FleshCultRuleComponent component,
|
||||
GameRuleComponent gameRule,
|
||||
GameRuleStartedEvent args)
|
||||
{
|
||||
var eligible = new List<Entity<StationEventEligibleComponent, NpcFactionMemberComponent>>();
|
||||
var eligibleQuery = EntityQueryEnumerator<StationEventEligibleComponent, NpcFactionMemberComponent>();
|
||||
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<FleshCultistComponent>(cultist);
|
||||
}
|
||||
QueueDel(uid);
|
||||
}
|
||||
|
||||
private void OnObjectivesTextPrepend(EntityUid uid, FleshCultRuleComponent comp, ref ObjectivesTextPrependEvent args)
|
||||
|
|
@ -48,121 +87,37 @@ public sealed class FleshCultRuleSystem : GameRuleSystem<FleshCultRuleComponent>
|
|||
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<FleshCultRuleComponent, GameRuleComponent>();
|
||||
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<FleshCultRuleComponent, GameRuleComponent>();
|
||||
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<FleshCultRuleComponent, GameRuleComponent>();
|
||||
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<FleshCultRuleComponent>
|
|||
MakeCultist(args.EntityUid, 15, ent.Comp);
|
||||
}
|
||||
|
||||
public void MakeCultistAdmin(EntityUid target, FixedPoint2 startingPoints)
|
||||
private void OnAfterAntagSelectionComplete(Entity<FleshCultRuleComponent> 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<AntagSelectionComponent> antagSelection)
|
||||
{
|
||||
EntityUid? leader = null;
|
||||
foreach (var compSelectedMind in antagSelection.Comp.SelectedMinds)
|
||||
{
|
||||
if (!TryComp<MindComponent>(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<FleshCultRuleComponent>
|
|||
_store.TryAddCurrency(new Dictionary<string, FixedPoint2>
|
||||
{ {fleshCultistComponent.StolenCurrencyPrototype, startingPoints} }, mind.OwnedEntity.Value);
|
||||
|
||||
fleshCultRule.Cultists.Add(fleshCultist);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +261,14 @@ public sealed class FleshCultRuleSystem : GameRuleSystem<FleshCultRuleComponent>
|
|||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ public sealed partial class CCVars
|
|||
/// If roles should be restricted based on time.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool>
|
||||
GameRoleTimers = CVarDef.Create("game.role_timers", true, CVar.SERVER | CVar.REPLICATED);
|
||||
GameRoleTimers = CVarDef.Create("game.role_timers", false, CVar.SERVER | CVar.REPLICATED); // Sunrise-Edit
|
||||
|
||||
/// <summary>
|
||||
/// Override default role requirements using a <see cref="JobRequirementOverridePrototype"/>
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ public partial class ListingData : IEquatable<ListingData>
|
|||
/// The event that is broadcast when the listing is purchased.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[NonSerialized] // Sunrise-Edit
|
||||
public object? ProductEvent;
|
||||
|
||||
[DataField]
|
||||
|
|
|
|||
|
|
@ -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<ProtoId<TagPrototype>>? Tags;
|
||||
|
||||
// Sunrise-Start
|
||||
public List<ProtoId<SpeciesPrototype>>? Species;
|
||||
// Sunrise-End
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
|
|
|
|||
|
|
@ -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<HumanoidAppearanceComponent>(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
|
||||
|
|
|
|||
|
|
@ -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<EntityPrototype>))]
|
||||
public string BulletAcidSpawnId = "BulletSplashAcid";
|
||||
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ActionFleshCultistShop = "FleshCultistShop";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("speciesWhitelist")]
|
||||
public List<string> 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<string> 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<CurrencyPrototype>))]
|
||||
public string StolenCurrencyPrototype = "StolenMutationPoint";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("fleshBladeSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string BladeSpawnId = "FleshBlade";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("fleshFistSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string FistSpawnId = "FleshFist";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("clawSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ClawSpawnId = "FleshClaw";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("spikeHandGunSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string SpikeHandGunSpawnId = "FleshSpikeHandGun";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("armorSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ArmorSpawnId = "ClothingOuterArmorFlesh";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("heavyArmorSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string HeavyArmorSpawnId = "ClothingOuterHeavyArmorFlesh";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("spiderLegsSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string SpiderLegsSpawnId = "ClothingFleshSpiderLegs";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite),
|
||||
DataField("fleshMutationMobId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
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<EntityPrototype>)),
|
||||
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<EntityPrototype>))]
|
||||
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<AlertPrototype> MutationPointAlert = "MutationPoint";
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier SoundMutation = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/flesh_cultist_mutation.ogg");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,36 +11,19 @@ namespace Content.Shared.Flesh
|
|||
{
|
||||
[DataField("transformSound")] public SoundSpecifier TransformSound = new SoundCollectionSpecifier("gib");
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("speciesWhitelist")]
|
||||
public List<string> 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<string, float> 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<EntityUid> EdgeMobs = new();
|
||||
|
||||
|
|
@ -93,7 +76,7 @@ namespace Content.Shared.Flesh
|
|||
}
|
||||
}
|
||||
|
||||
public enum HeartStates
|
||||
public enum HeartStatus
|
||||
{
|
||||
Base,
|
||||
Active,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ public enum FleshHeartVisuals : byte
|
|||
[Serializable, NetSerializable]
|
||||
public enum FleshHeartStatus
|
||||
{
|
||||
Base,
|
||||
Active,
|
||||
Disable,
|
||||
Final,
|
||||
Destruction
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<FactionIconPrototype> 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
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string> CheckSlots = [];
|
||||
}
|
||||
|
||||
public sealed partial class FleshCultistUnlockAbilityEvent : InstantActionEvent
|
||||
{
|
||||
[DataField]
|
||||
public EntProtoId Prototype = string.Empty;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
namespace Content.Shared._Sunrise.FleshCult;
|
||||
|
||||
public abstract class SharedFleshCultistSystem : EntitySystem
|
||||
{
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FleshHuggerComponent, BeingUnequippedAttemptEvent>(OnUnequipAttempt);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FleshHuggerComponent, BeingUnequippedAttemptEvent>(OnUnequipAttempt);
|
||||
}
|
||||
|
||||
private void OnUnequipAttempt(EntityUid uid, FleshHuggerComponent component, BeingUnequippedAttemptEvent args)
|
||||
{
|
||||
if (args.Slot != "mask")
|
||||
return;
|
||||
if (component.EquipedOn != args.Unequipee)
|
||||
return;
|
||||
if (HasComp<FleshCultistComponent>(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<FleshCultistComponent>(args.Unequipee))
|
||||
return;
|
||||
_popup.PopupEntity(Loc.GetString("flesh-pudge-throw-hugger-try-unequip"),
|
||||
args.Unequipee,
|
||||
args.Unequipee,
|
||||
PopupType.Large);
|
||||
args.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<FleshMobComponent, AttackAttemptEvent>(OnAttackAttempt);
|
||||
}
|
||||
|
||||
private void OnAttackAttempt(EntityUid uid, FleshMobComponent component, AttackAttemptEvent args)
|
||||
private void OnAttackAttempt(Entity<FleshMobComponent> fleshMob, ref AttackAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
if (args.Cancelled || args.Target == null)
|
||||
return;
|
||||
|
||||
if (HasComp<FleshMobComponent>(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<FleshCultistComponent>(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<FleshHeartComponent>(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<FleshMobComponent> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = Вы сможете спокойно работать с электричеством не опасаясь поражения напряжением.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -7,4 +7,4 @@
|
|||
- type: InstantAction
|
||||
icon: _Sunrise/FleshCult/Interface/Actions/flesh_web.png
|
||||
event: !type:SpiderWebActionEvent
|
||||
useDelay: 30
|
||||
useDelay: 15
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
- Prying
|
||||
useSound: /Audio/Effects/gib2.ogg
|
||||
- type: Prying
|
||||
speedModifier: 2.5
|
||||
speedModifier: 5
|
||||
pryPowered: true
|
||||
force: true
|
||||
- type: MeleeWeapon
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
issuer: flesh-cult
|
||||
- type: RoleRequirement
|
||||
roles:
|
||||
components:
|
||||
- FleshCultistRole
|
||||
mindRoles:
|
||||
- FleshCultistRole
|
||||
|
||||
- type: entity
|
||||
categories: [ HideSpawnMenu ]
|
||||
|
|
|
|||
|
|
@ -23,6 +23,19 @@
|
|||
- type: NpcFactionMember
|
||||
factions:
|
||||
- FleshHuman
|
||||
whitelist:
|
||||
species:
|
||||
- Human
|
||||
- Reptilian
|
||||
- Dwarf
|
||||
- Vulpkanin
|
||||
- Felinid
|
||||
- Moth
|
||||
- Arachnid
|
||||
- Swine
|
||||
- Demon
|
||||
- Vox
|
||||
- Tajaran
|
||||
blacklist:
|
||||
components:
|
||||
- AntagImmune
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue