Культ плоти (#871)

* Культ плоти

* Мейби это скорет ноги для паучих ног

* фикс ног и иконок (#986)

Co-authored-by: Vigers Ray <vigersray@gmail.com>

---------

Co-authored-by: Babaev <129369024+babaevlsdd@users.noreply.github.com>
This commit is contained in:
Vigers Ray 2025-01-01 08:02:29 +03:00 committed by GitHub
parent a7b97e299f
commit 6304ac7e6f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
224 changed files with 6813 additions and 77 deletions

View file

@ -0,0 +1,32 @@
using Content.Shared._Sunrise.FleshCult;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
namespace Content.Client._Sunrise.FleshCult;
public sealed class FleshCultistSystem : SharedFleshMobSystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FleshCultistComponent, GetStatusIconsEvent>(GetFleshCultistIcon);
SubscribeLocalEvent<FleshMobComponent, GetStatusIconsEvent>(GetFleshMobIcon);
}
private void GetFleshCultistIcon(Entity<FleshCultistComponent> ent, ref GetStatusIconsEvent args)
{
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
args.StatusIcons.Add(iconPrototype);
}
private void GetFleshMobIcon(Entity<FleshMobComponent> ent, ref GetStatusIconsEvent args)
{
if (HasComp<FleshCultistComponent>(ent))
return;
var iconPrototype = _prototype.Index(ent.Comp.StatusIcon);
args.StatusIcons.Add(iconPrototype);
}
}

View file

@ -0,0 +1,23 @@
using Content.Shared._Sunrise.FleshCult;
using Content.Shared.Flesh;
using Robust.Client.GameObjects;
namespace Content.Client._Sunrise.FleshCult;
public sealed class FleshHeartSystem : VisualizerSystem<FleshHeartComponent>
{
protected override void OnAppearanceChange(EntityUid uid, FleshHeartComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
if (!AppearanceSystem.TryGetData<FleshHeartStatus>(uid, FleshHeartVisuals.State, out var state, args.Component))
return;
var layer = args.Sprite.LayerMapGet(FleshHeartLayers.Base);
if (state == FleshHeartStatus.Active)
{
args.Sprite.LayerSetState(layer, component.FinalState);
}
}
}

View file

@ -0,0 +1,11 @@
using Content.Server.Flesh;
namespace Content.Client._Sunrise.FleshCult;
public sealed class FleshHuggerSystem: SharedFleshHuggerSystem
{
public override void Initialize()
{
}
}

View file

@ -0,0 +1,11 @@
using Content.Shared._Sunrise.FleshCult;
namespace Content.Client._Sunrise.FleshCult;
public sealed class FleshMobSystem : SharedFleshCultistSystem
{
public override void Initialize()
{
base.Initialize();
}
}

View file

@ -1,4 +1,5 @@
using Content.Server._Sunrise.AssaultOps;
using Content.Server._Sunrise.FleshCult.GameRule;
using Content.Server.Administration.Commands;
using Content.Server.Antag;
using Content.Server.GameTicking.Rules.Components;
@ -194,5 +195,19 @@ public sealed partial class AdminVerbSystem
Message = Loc.GetString("admin-verb-make-assault-operative"),
};
args.Verbs.Add(assaultOperative);
Verb fleshCultist = new()
{
Text = "Make Flesh Cultist",
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Texture(new ResPath("_Sunrise/FleshCult/Interface/Actions/fleshCultistFleshHeart.png")),
Act = () =>
{
_antag.ForceMakeAntag<FleshCultRuleComponent>(targetPlayer, "FleshCult");
},
Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-flesh-cultist"),
};
args.Verbs.Add(fleshCultist);
}
}

View file

@ -92,5 +92,12 @@ namespace Content.Server.Destructible.Thresholds
behavior.Execute(owner, system, cause);
}
}
// Sunrise-Start
public void AddBehavior(IThresholdBehavior behavior)
{
_behaviors.Add(behavior);
}
// Sunrise-End
}
}

View file

@ -0,0 +1,18 @@
using Content.Server.Sunrise.FleshCult;
using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
namespace Content.Server._Sunrise.FleshCult;
public sealed partial class CauseFleshCultInfection : EntityEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-cause-flesh-cultist-infection", ("chance", Probability));
public override void Effect(EntityEffectBaseArgs args)
{
var entityManager = args.EntityManager;
entityManager.EnsureComponent<PendingFleshCultistComponent>(args.TargetEntity);
}
}

View file

@ -0,0 +1,8 @@
namespace Content.Server._Sunrise.FleshCult.Events;
[RegisterComponent, Access(typeof(VentFleshWormsRule))]
public sealed partial class VentFleshWormsRuleComponent : Component
{
[DataField("spawnedPrototypeWorm")]
public string SpawnedPrototypeWorm = "MobFleshWorm";
}

View file

@ -0,0 +1,47 @@
using System.Linq;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Server.StationEvents.Components;
using Content.Server.StationEvents.Events;
using Content.Shared.GameTicking.Components;
using Robust.Shared.Utility;
namespace Content.Server._Sunrise.FleshCult.Events;
public sealed class VentFleshWormsRule : StationEventSystem<VentFleshWormsRuleComponent>
{
[Dependency] private readonly StationSystem _stationSystem = default!;
protected override void Started(EntityUid uid, VentFleshWormsRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
var targetStation = _stationSystem.GetStations().FirstOrNull();
if (!TryComp(targetStation, out StationDataComponent? data))
{
Logger.Info("TargetStation not have StationDataComponent");
return;
}
var spawnLocations = EntityManager.EntityQuery<VentCritterSpawnLocationComponent, TransformComponent>().ToList();
var grids = data.Grids.ToHashSet();
spawnLocations.RemoveAll(
backupSpawnLoc =>
backupSpawnLoc.Item2.GridUid.HasValue && !grids.Contains(backupSpawnLoc.Item2.GridUid.Value));
RobustRandom.Shuffle(spawnLocations);
var spawnAmount = RobustRandom.Next(10, 20);
Sawmill.Info($"Spawning {spawnAmount} of {component.SpawnedPrototypeWorm}");
foreach (var location in spawnLocations)
{
if (spawnAmount-- == 0)
break;
var coords = Transform(location.Item1.Owner);
Spawn(component.SpawnedPrototypeWorm, coords.Coordinates);
}
}
}

View file

@ -0,0 +1,10 @@
using Content.Shared.Roles;
namespace Content.Server._Sunrise.FleshCult;
[RegisterComponent]
public sealed partial class FleshCultistRoleComponent : BaseMindRoleComponent
{
[DataField("fleshHearts")]
public int FleshHearts;
}

View file

@ -0,0 +1,644 @@
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);
}
}

View file

@ -0,0 +1,808 @@
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;
}
}

View file

@ -0,0 +1,20 @@
namespace Content.Server._Sunrise.FleshCult.FleshGrowth;
[RegisterComponent, Access(typeof(SpreaderFleshSystem), typeof(FleshHeartSystem))]
public sealed partial class SpreaderFleshComponent : Component
{
[DataField("chance", required: true)]
public float Chance = 1f;
[DataField("growthResult", required: true)]
public string GrowthResult = "Flesh";
[DataField("wallResult", required: true)]
public string WallResult = "WallFlesh";
[DataField("enabled")]
public bool Enabled = true;
[DataField("source")]
public EntityUid? Source;
}

View file

@ -0,0 +1,213 @@
using System.Linq;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Destructible;
using Content.Server.Destructible.Thresholds;
using Content.Server.Destructible.Thresholds.Behaviors;
using Content.Server.Destructible.Thresholds.Triggers;
using Content.Shared.Atmos;
using Content.Shared.Destructible.Thresholds;
using Content.Shared.Tag;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server._Sunrise.FleshCult.FleshGrowth;
// Future work includes making the growths per interval thing not global, but instead per "group"
public sealed class SpreaderFleshSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
/// <summary>
/// Maximum number of edges that can grow out every interval.
/// </summary>
private const int GrowthsPerInterval = 5;
private float _accumulatedFrameTime;
private readonly HashSet<EntityUid> _edgeGrowths = new();
public override void Initialize()
{
SubscribeLocalEvent<SpreaderFleshComponent, ComponentAdd>(SpreaderAddHandler);
SubscribeLocalEvent<AirtightChanged>(OnAirtightChanged);
}
private void OnAirtightChanged(ref AirtightChanged ev)
{
UpdateNearbySpreaders(ev.Entity, ev.Airtight);
}
private void SpreaderAddHandler(EntityUid uid, SpreaderFleshComponent component, ComponentAdd args)
{
if (component.Enabled)
_edgeGrowths.Add(uid); // ez
}
public void UpdateNearbySpreaders(EntityUid blocker, AirtightComponent comp)
{
if (!EntityManager.TryGetComponent<TransformComponent>(blocker, out var transform))
return; // how did we get here?
if (!TryComp<MapGridComponent>(transform.GridUid, out var grid))
return;
var spreaderQuery = GetEntityQuery<SpreaderFleshComponent>();
var tile = grid.TileIndicesFor(transform.Coordinates);
for (var i = 0; i < Atmospherics.Directions; i++)
{
var direction = (AtmosDirection) (1 << i);
if (!comp.AirBlockedDirection.IsFlagSet(direction))
continue;
var directionEnumerator =
grid.GetAnchoredEntitiesEnumerator(SharedMapSystem.GetDirection(tile, direction.ToDirection()));
while (directionEnumerator.MoveNext(out var ent))
{
if (spreaderQuery.TryGetComponent(ent, out var s) && s.Enabled)
_edgeGrowths.Add(ent.Value);
}
}
}
public override void Update(float frameTime)
{
_accumulatedFrameTime += frameTime;
if (!(_accumulatedFrameTime >= 1.0f))
return;
_accumulatedFrameTime -= 1.0f;
var growthList = _edgeGrowths.ToList();
_robustRandom.Shuffle(growthList);
var successes = 0;
foreach (var entity in growthList)
{
if (!TryGrow(entity))
continue;
successes += 1;
if (successes >= GrowthsPerInterval)
break;
}
}
private bool TryGrow(EntityUid ent, TransformComponent? transform = null, SpreaderFleshComponent? spreader = null)
{
if (!Resolve(ent, ref transform, ref spreader, false))
return false;
if (spreader.Enabled == false)
return false;
if (!TryComp<MapGridComponent>(transform.GridUid, out var grid))
return false;
var didGrow = false;
for (var i = 0; i < 4; i++)
{
var direction = (DirectionFlag) (1 << i);
var coords = transform.Coordinates.Offset(direction.AsDir().ToVec());
if (grid.GetTileRef(coords).Tile.IsEmpty || _robustRandom.Prob(1 - spreader.Chance))
continue;
var ents = _mapSystem.GetLocal(transform.GridUid.Value, grid, coords);
var entityUids = ents as EntityUid[] ?? ents.ToArray();
if (entityUids.Any(x => IsTileBlockedFrom(x, direction)))
continue;
var canSpawnWall = true;
var canSpawnFloor = true;
string entityStrucrureId = String.Empty;
foreach (var entityUid in entityUids)
{
if (_tagSystem.HasAnyTag(entityUid, "Wall", "Window"))
{
if (!_tagSystem.HasAnyTag(entityUid, "Directional"))
{
if (TryComp(entityUid, out MetaDataComponent? metaData))
{
if (metaData.EntityPrototype != null)
entityStrucrureId = metaData.EntityPrototype.ID;
}
canSpawnFloor = false;
}
}
if (_tagSystem.HasAnyTag(entityUid, "Flesh", "Directional"))
{
canSpawnWall = false;
}
}
if (canSpawnFloor)
{
didGrow = true;
var fleshFloor = EntityManager.SpawnEntity(spreader.GrowthResult,
transform.Coordinates.Offset(direction.AsDir().ToVec()));
var spreaderFleshComponent = EnsureComp<SpreaderFleshComponent>(fleshFloor);
spreaderFleshComponent.Source = spreader.Source;
}
else
{
if (!canSpawnWall)
continue;
didGrow = true;
var fleshWall = EntityManager.SpawnEntity(spreader.WallResult,
transform.Coordinates.Offset(direction.AsDir().ToVec()));
var spreaderFleshComponent = EnsureComp<SpreaderFleshComponent>(fleshWall);
spreaderFleshComponent.Source = spreader.Source;
if (EntityManager.TryGetComponent(fleshWall, out DestructibleComponent? destructible))
{
destructible.Thresholds.Clear();
var damageThreshold = new DamageThreshold
{
Trigger = new DamageTrigger { Damage = 5 }
};
damageThreshold.AddBehavior(new SpawnEntitiesBehavior
{
Spawn = new Dictionary<EntProtoId, MinMax> { { entityStrucrureId, new MinMax{Min = 1, Max = 1} } },
Offset = 0f
});
damageThreshold.AddBehavior(new DoActsBehavior
{
Acts = ThresholdActs.Destruction
});
destructible.Thresholds.Add(damageThreshold);
}
foreach (var entityUid in entityUids)
{
if (_tagSystem.HasAnyTag(entityUid, "Wall", "Window"))
EntityManager.DeleteEntity(entityUid);
}
}
}
return didGrow;
}
private bool IsTileBlockedFrom(EntityUid ent, DirectionFlag dir)
{
if (EntityManager.TryGetComponent<SpreaderFleshComponent>(ent, out _))
return true;
if (!EntityManager.TryGetComponent<AirtightComponent>(ent, out var airtight))
return false;
// var oppositeDir = dir.AsDir().GetOpposite().ToAtmosDirection();
// return airtight.AirBlocked && airtight.AirBlockedDirection.IsFlagSet(oppositeDir);
return false;
}
}

View file

@ -0,0 +1,8 @@
namespace Content.Server._Sunrise.FleshCult
{
[RegisterComponent]
public sealed partial class FleshHandModComponent : Component
{
}
}

View file

@ -0,0 +1,466 @@
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;
}
}
}

View file

@ -0,0 +1,283 @@
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);
}
}
}
}

View file

@ -0,0 +1,38 @@
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);
}
}
}
}

View file

@ -0,0 +1,53 @@
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");
}
}

View file

@ -0,0 +1,155 @@
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);
}
}
}

View file

@ -0,0 +1,66 @@
using Content.Shared._Sunrise.FleshCult;
using Content.Shared.NPC.Prototypes;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server._Sunrise.FleshCult.GameRule;
[RegisterComponent, Access(typeof(FleshCultRuleSystem))]
public sealed partial class FleshCultRuleComponent : Component
{
public EntityUid CultistsLeaderMind = new();
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";
[DataField("fleshCultistLeaderPrototypeID", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
public string FleshCultistLeaderPrototypeId = "FleshCultistLeader";
[DataField("faction", customTypeSerializer: typeof(PrototypeIdSerializer<NpcFactionPrototype>), required: true)]
public string Faction = default!;
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",
"Reptilian",
"Dwarf",
"Vulpkanin",
"Felinid",
"Moth",
"Swine",
"Arachnid"
};
public enum WinTypes
{
FleshHeartFinal,
AllCultistsDead,
Fail
}
public TimeSpan AnnounceAt = TimeSpan.Zero;
public Dictionary<ICommonSession, HumanoidCharacterProfile> StartCandidates = new();
}

View file

@ -0,0 +1,296 @@
using System.Linq;
using Content.Server.Antag;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.Mind;
using Content.Server.Objectives;
using Content.Server.RoundEnd;
using Content.Server.Station.Components;
using Content.Server.Store.Systems;
using Content.Shared._Sunrise.FleshCult;
using Content.Shared.FixedPoint;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mind;
using Content.Shared.NPC.Systems;
using Content.Shared.Roles;
using Robust.Shared.Audio.Systems;
namespace Content.Server._Sunrise.FleshCult.GameRule;
public sealed class FleshCultRuleSystem : GameRuleSystem<FleshCultRuleComponent>
{
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
[Dependency] private readonly StoreSystem _store = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
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);
}
private void OnObjectivesTextPrepend(EntityUid uid, FleshCultRuleComponent comp, ref ObjectivesTextPrependEvent args)
{
if (!TryComp(comp.CultistsLeaderMind, out MindComponent? mind) && mind == null)
return;
_mindSystem.TryGetSession(comp.CultistsLeaderMind, out var session);
args.Text += "\n" + Loc.GetString("flesh-cult-round-end-leader", ("name", mind.CharacterName)!, ("username", session!.Name));
}
private void OnFleshHeartActivate(FleshHeartSystem.FleshHeartActivateEvent 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)
{
continue;
}
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)
{
continue;
}
if (fleshCult.FleshHearts.ContainsKey(ev.FleshHeardUid))
{
fleshCult.FleshHearts[ev.FleshHeardUid] = FleshHeartStatus.Destruction;
}
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)
{
continue;
}
fleshCult.WinType = FleshCultRuleComponent.WinTypes.FleshHeartFinal;
_roundEndSystem.EndRound();
return;
}
}
}
private void AfterEntitySelected(Entity<FleshCultRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
MakeCultist(args.EntityUid, 15, ent.Comp);
}
public void MakeCultistAdmin(EntityUid target, FixedPoint2 startingPoints)
{
var fleshCultRule = StartGameRule();
MakeCultist(target, startingPoints, fleshCultRule);
}
public FleshCultRuleComponent StartGameRule()
{
var comp = EntityQuery<FleshCultRuleComponent>().FirstOrDefault();
if (comp == null)
{
GameTicker.StartGameRule("FleshCult", out var ruleEntity);
comp = Comp<FleshCultRuleComponent>(ruleEntity);
}
return comp;
}
public bool MakeCultist(EntityUid fleshCultist, FixedPoint2 startingPoints, FleshCultRuleComponent fleshCultRule)
{
if (!_mindSystem.TryGetMind(fleshCultist, out var mindId, out var mind))
return false;
if (mind.OwnedEntity is not { } entity)
{
Logger.ErrorS("preset", "Mind picked for cultist did not have an attached entity.");
return false;
}
SendCultistBriefing(mindId, fleshCultRule.CultistsNames);
if (_mindSystem.TryGetSession(mindId, out var session))
{
_audioSystem.PlayGlobal(fleshCultRule.AddedSound, session);
}
_npcFaction.RemoveFaction(entity, "NanoTrasen", false);
_npcFaction.AddFaction(entity, "FleshHuman");
var fleshCultistComponent = EnsureComp<FleshCultistComponent>(mind.OwnedEntity.Value);
_store.TryAddCurrency(new Dictionary<string, FixedPoint2>
{ {fleshCultistComponent.StolenCurrencyPrototype, startingPoints} }, mind.OwnedEntity.Value);
return true;
}
private void SendCultistBriefing(EntityUid mind, List<string> cultistsNames)
{
if (!_mindSystem.TryGetSession(mind, out var session))
return;
_chatManager.DispatchServerMessage(session, Loc.GetString("flesh-cult-role-greeting"));
_chatManager.DispatchServerMessage(session, Loc.GetString("flesh-cult-role-cult-members", ("cultMembers", string.Join(", ", cultistsNames))));
}
private void SendCultistLeaderBriefing(EntityUid mind, List<string> cultistsNames)
{
if (!_mindSystem.TryGetSession(mind, out var session))
return;
_chatManager.DispatchServerMessage(session, Loc.GetString("flesh-cult-role-greeting-leader"));
_chatManager.DispatchServerMessage(session, Loc.GetString("flesh-cult-role-cult-members", ("cultMembers", string.Join(", ", cultistsNames))));
}
protected override void AppendRoundEndText(EntityUid uid,
FleshCultRuleComponent component,
GameRuleComponent gameRule,
ref RoundEndTextAppendEvent args)
{
var result = Loc.GetString("flesh-cult-round-end-count-create-flesh-hearts", ("heartsCount",
component.FleshHearts.Count));
var destroyHearts = 0;
var activateHearts = 0;
foreach (var (heartUid, heartStatus) in component.FleshHearts)
{
switch (heartStatus)
{
case FleshHeartStatus.Destruction:
destroyHearts++;
break;
case FleshHeartStatus.Active:
activateHearts++;
break;
}
}
if (component.FleshHearts.Count > 0)
{
if (activateHearts > 0)
{
result += "\n" + Loc.GetString("flesh-cult-round-end-count-activate-flesh-hearts", ("heartsCount",
activateHearts));
}
else
{
result += "\n" + Loc.GetString("flesh-cult-round-end-count-no-activate-flesh-hearts");
}
if (destroyHearts > 0)
{
result += "\n" + Loc.GetString("flesh-cult-round-end-count-destroy-flesh-hearts", ("heartsCount",
destroyHearts));
}
else
{
result += "\n" + Loc.GetString("flesh-cult-round-end-count-no-destroy-flesh-hearts");
}
}
if (component.FleshHeartActive)
{
result += "\n" + Loc.GetString("flesh-cult-round-end-flesh-heart-succes");
}
else
{
result += "\n" + Loc.GetString("flesh-cult-round-end-flesh-heart-fail");
}
args.AddLine("\n" + result);
}
}

View file

@ -0,0 +1,9 @@
using Content.Server._Sunrise.FleshCult.Objectives;
using Content.Server.Objectives.Systems;
namespace Content.Server.Objectives.Components;
[RegisterComponent, Access(typeof(FleshCultConditionsSystem))]
public sealed partial class CreateFleshHeartConditionComponent : Component
{
}

View file

@ -0,0 +1,41 @@
using Content.Server._Sunrise.FleshCult.GameRule;
using Content.Server.Objectives.Components;
using Content.Server.Objectives.Systems;
using Content.Shared.GameTicking.Components;
using Content.Shared.Objectives.Components;
namespace Content.Server._Sunrise.FleshCult.Objectives;
public sealed class FleshCultConditionsSystem : EntitySystem
{
[Dependency] private readonly NumberObjectiveSystem _number = default!;
public override void Initialize()
{
SubscribeLocalEvent<CreateFleshHeartConditionComponent, ObjectiveGetProgressEvent>(OnFleshHeartGetProgress);
}
private void OnFleshHeartGetProgress(EntityUid uid, CreateFleshHeartConditionComponent comp, ref ObjectiveGetProgressEvent args)
{
args.Progress = FleshHeartProgress(args.MindId, _number.GetTarget(uid));
}
private float FleshHeartProgress(EntityUid? mindId, int target)
{
// prevent divide-by-zero
if (target == 0)
return 1f;
if (!TryComp<FleshCultistRoleComponent>(mindId, out var role))
return 0f;
var query = EntityQueryEnumerator<FleshCultRuleComponent, GameRuleComponent>();
while (query.MoveNext(out var uid, out var fleshCult, out var gameRule))
{
return fleshCult.FleshHeartActive ? 1f : 0f;
}
return 0f;
}
}

View file

@ -0,0 +1,59 @@
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Sunrise.FleshCult;
[RegisterComponent]
public sealed partial class PendingFleshCultistComponent : Component
{
[DataField("firstStageTimer")]
public float FirstStageTimer = 30;
[DataField("secondStageTimer")]
public float SecondStageTimer = 30;
[DataField("currentStage")]
public PendingFleshCultistStage CurrentStage;
[DataField("nextParalyze", customTypeSerializer:typeof(TimeOffsetSerializer))]
public TimeSpan NextParalyze;
[DataField("paralyzeInterval")]
public float ParalyzeInterval = 10;
[DataField("paralyzeTime")]
public float ParalyzeTime = 5;
[DataField("nextScream", customTypeSerializer:typeof(TimeOffsetSerializer))]
public TimeSpan NextScream;
[DataField("screamInterval")]
public float ScreamInterval = 5;
[DataField("nextStutter", customTypeSerializer:typeof(TimeOffsetSerializer))]
public TimeSpan NextStutter;
[DataField("stutterTime")]
public float StutterTime = 10;
[DataField("stutterInterval")]
public float StutterInterval = 3;
[DataField("nextJitter", customTypeSerializer:typeof(TimeOffsetSerializer))]
public TimeSpan NextJitter;
[DataField("jitterTime")]
public float JitterTime = 10;
[DataField("jitterInterval")]
public float JitterInterval = 5;
public float Accumulator = 0;
}
public enum PendingFleshCultistStage
{
First,
Second,
Third,
Final,
}

View file

@ -0,0 +1,13 @@
using Content.Shared.Chemistry.Components;
namespace Content.Server._Sunrise.SplashOnTrigger
{
[RegisterComponent]
internal sealed partial class SplashOnTriggerComponent : Component
{
[DataField("splashReagents")] public Solution SplashReagents = new()
{
};
}
}

View file

@ -0,0 +1,44 @@
using Content.Server.Explosion.EntitySystems;
using Content.Server.Fluids.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using JetBrains.Annotations;
namespace Content.Server._Sunrise.SplashOnTrigger;
[UsedImplicitly]
public sealed partial class SplashOnTriggerSystem : EntitySystem
{
[Dependency] private readonly SharedSolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly PuddleSystem _puddleSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SplashOnTriggerComponent, TriggerEvent>(OnSplashTrigger);
}
private void OnSplashTrigger(EntityUid uid, SplashOnTriggerComponent component, TriggerEvent args)
{
var xform = Transform(uid);
var coords = xform.Coordinates;
if (!coords.IsValid(EntityManager))
return;
var transferSolution = new Solution();
foreach (var solution in component.SplashReagents)
{
transferSolution.AddReagent(solution.Reagent, solution.Quantity);
}
if (_solutionSystem.TryGetInjectableSolution(uid, out var injectableSolution, out _))
{
_solutionSystem.TryAddSolution(injectableSolution.Value, transferSolution);
}
_puddleSystem.TrySplashSpillAt(uid, coords, transferSolution, out var puddleUid);
}
}

View file

@ -0,0 +1,142 @@
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;
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, NetworkedComponent]
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
[ViewVariables(VVAccess.ReadWrite), DataField("maxHunger")]
public FixedPoint2 MaxHunger = 200;
[ViewVariables(VVAccess.ReadWrite),
DataField("bulletAcidSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string BulletAcidSpawnId = "BulletSplashAcid";
[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("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");
[ViewVariables] public float Accumulator = 0;
[ViewVariables] public float AccumulatorStarveNotify = 0;
[DataField("fleshStatusIcon")]
public ProtoId<FactionIconPrototype> StatusIcon { get; set; } = "FleshFaction";
[DataField]
public ProtoId<AlertPrototype> MutationPointAlert = "MutationPoint";
}

View file

@ -0,0 +1,101 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Flesh
{
[RegisterComponent]
public sealed partial class FleshHeartComponent : Component
{
[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
[DataField("spawnObjectsFrequency"), ViewVariables(VVAccess.ReadWrite)]
public float SpawnObjectsFrequency = 60;
[DataField("spawnObjectsAmount"), ViewVariables(VVAccess.ReadWrite)]
public int SpawnObjectsAmount = 6;
[DataField("spawnObjectsRadius"), ViewVariables(VVAccess.ReadWrite)]
public float SpawnObjectsRadius = 5;
[DataField("fleshTileId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>)),
ViewVariables(VVAccess.ReadWrite)]
public string FleshTileId = "Flesh";
[DataField("spawns"), ViewVariables(VVAccess.ReadWrite)]
public Dictionary<string, float> Spawns = new();
[DataField("spawnMobsFrequency"), ViewVariables(VVAccess.ReadWrite)]
public float SpawnMobsFrequency = 120;
[DataField("spawnMobsAmount"), ViewVariables(VVAccess.ReadWrite)]
public int SpawnMobsAmount = 10;
[DataField("spawnMobsRadius"), ViewVariables(VVAccess.ReadWrite)]
public float SpawnMobsRadius = 3;
[ViewVariables]
public float Accumulator = 0;
[ViewVariables]
public float SpawnMobsAccumulator = 110;
[ViewVariables]
public float SpawnObjectsAccumulator = 0;
[ViewVariables]
public float FinalStageAccumulator = 0;
[ViewVariables]
public HeartStates State = HeartStates.Base;
public readonly HashSet<EntityUid> EdgeMobs = new();
[DataField("damageMobsIfHeartDestruct", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier DamageMobsIfHeartDestruct = default!;
[DataField("finalState")]
public string? FinalState = "underpowered";
}
}
public enum HeartStates
{
Base,
Active,
Disable
}

View file

@ -0,0 +1,23 @@
using Robust.Shared.Serialization;
namespace Content.Shared._Sunrise.FleshCult;
[Serializable, NetSerializable]
public enum FleshHeartVisuals : byte
{
State
}
[Serializable, NetSerializable]
public enum FleshHeartStatus
{
Active,
Disable,
Destruction
}
[Serializable, NetSerializable]
public enum FleshHeartLayers : byte
{
Base
}

View file

@ -0,0 +1,42 @@
using Content.Server.Flesh;
using Content.Shared.Damage;
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
{
[Access(typeof(SharedFleshHuggerSystem))]
[RegisterComponent, NetworkedComponent]
public sealed partial class FleshHuggerComponent : Component
{
[DataField("actionJump", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string ActionFleshHuggerJumpId = "FleshHuggerJump";
[DataField("actionGetOff", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string ActionFleshHuggerGetOffId = "FleshHuggerGetOff";
[DataField("paralyzeTime"), ViewVariables(VVAccess.ReadWrite)]
public float ParalyzeTime = 3f;
[DataField("chansePounce"), ViewVariables(VVAccess.ReadWrite)]
public int ChansePounce = 33;
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier Damage = default!;
public bool IsDeath = false;
public EntityUid EquipedOn;
[ViewVariables] public float Accumulator = 0;
[DataField("damageFrequency"), ViewVariables(VVAccess.ReadWrite)]
public float DamageFrequency = 5;
[ViewVariables(VVAccess.ReadWrite), DataField("soundJump")]
public SoundSpecifier? SoundJump = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/flesh_worm_scream.ogg");
}
}

View file

@ -0,0 +1,42 @@
using Content.Shared.Actions;
using Content.Shared.StatusIcon;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared._Sunrise.FleshCult
{
[RegisterComponent]
public sealed partial class FleshMobComponent : Component
{
[ViewVariables(VVAccess.ReadWrite), DataField("soundDeath")]
public SoundSpecifier? SoundDeath = new SoundPathSpecifier("/Audio/_Sunrise/FleshCult/flesh_pudge_dead.ogg");
[ViewVariables(VVAccess.ReadWrite),
DataField("deathMobSpawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string DeathMobSpawnId = "MobFleshWorm";
[DataField("deathMobSpawnCount"), ViewVariables(VVAccess.ReadWrite)]
public int DeathMobSpawnCount;
[DataField("fleshStatusIcon")]
public ProtoId<FactionIconPrototype> StatusIcon { get; set; } = "FleshFaction";
public bool IsDeath = false;
}
}
public sealed partial class FleshPudgeThrowFaceHuggerActionEvent : WorldTargetActionEvent
{
}
public sealed partial class FleshPudgeAcidSpitActionEvent : WorldTargetActionEvent
{
}
public sealed partial class FleshPudgeAbsorbBloodPoolActionEvent : InstantActionEvent
{
}

View file

@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Sunrise.FleshCult;
[RegisterComponent, NetworkedComponent]
public sealed partial class IgnoreFleshSpiderWebComponent : Component
{
}

View file

@ -0,0 +1,122 @@
using Content.Shared.Actions;
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._Sunrise.FleshCult;
[Serializable, NetSerializable]
public sealed partial class FleshCultistDevourDoAfterEvent : SimpleDoAfterEvent
{
}
[Serializable, NetSerializable]
public sealed partial class FleshCultistInfectionDoAfterEvent : SimpleDoAfterEvent
{
}
[Serializable, NetSerializable]
public sealed partial class FleshCultistInsulatedImmunityMutationEvent : SimpleDoAfterEvent
{
}
[Serializable, NetSerializable]
public sealed partial class FleshCultistPressureImmunityMutationEvent : SimpleDoAfterEvent
{
}
[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 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
{
}
public sealed partial class FleshCultistCreateFleshHeartActionEvent : InstantActionEvent
{
}
public sealed partial class FleshCultistThrowHuggerActionEvent : WorldTargetActionEvent
{
}
public sealed partial class FleshCultistAbsorbBloodPoolActionEvent : InstantActionEvent
{
}
public sealed partial class FleshCultistDevourActionEvent : EntityTargetActionEvent
{
}

View file

@ -0,0 +1,11 @@
namespace Content.Shared._Sunrise.FleshCult;
public abstract class SharedFleshCultistSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
}
}

View file

@ -0,0 +1,19 @@
using Content.Shared.DoAfter;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared._Sunrise.FleshCult
{
[NetworkedComponent()]
[Virtual]
public partial class SharedFleshHeartComponent : Component
{
[DataField("finalState")]
public string? FinalState = "underpowered";
}
[Serializable, NetSerializable]
public sealed partial class FleshHeartDragFinished : SimpleDoAfterEvent
{
}
}

View file

@ -0,0 +1,40 @@
using Content.Shared._Sunrise.FleshCult;
using Content.Shared.Actions;
using Content.Shared.Inventory.Events;
using Content.Shared.Popups;
namespace Content.Server.Flesh
{
public class SharedFleshHuggerSystem : EntitySystem
{
[Dependency] private readonly SharedPopupSystem _popup = default!;
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();
}
}
}
public sealed partial class FleshHuggerJumpActionEvent : WorldTargetActionEvent
{
};
public sealed partial class FleshHuggerGetOffFromFaceActionEvent : InstantActionEvent
{
};

View file

@ -0,0 +1,44 @@
using Content.Shared.Flesh;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
namespace Content.Shared._Sunrise.FleshCult;
public abstract class SharedFleshMobSystem : EntitySystem
{
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FleshMobComponent, AttackAttemptEvent>(OnAttackAttempt);
}
private void OnAttackAttempt(EntityUid uid, FleshMobComponent component, AttackAttemptEvent args)
{
if (args.Cancelled)
return;
if (HasComp<FleshMobComponent>(args.Target))
{
_popup.PopupCursor(Loc.GetString("flesh-mob-cant-atack-flesh-mob"), uid,
PopupType.LargeCaution);
args.Cancel();
}
if (HasComp<_Sunrise.FleshCult.FleshCultistComponent>(args.Target))
{
_popup.PopupCursor(Loc.GetString("flesh-mob-cant-atack-flesh-cultist"), uid,
PopupType.LargeCaution);
args.Cancel();
}
if (HasComp<FleshHeartComponent>(args.Target))
{
_popup.PopupCursor(Loc.GetString("flesh-mob-cant-atack-flesh-heart"), uid,
PopupType.LargeCaution);
args.Cancel();
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,2 +1,4 @@
ent-SyringeRomerolNT = { ent-BaseSyringe }
.desc = { ent-BaseSyringe.desc }
ent-SyringeCarolNT = { ent-BaseSyringe }
.desc = { ent-BaseSyringe.desc }

View file

@ -0,0 +1,2 @@
admin-verb-text-make-flesh-cultist = Сделать цель культистом плоти.
admin-verb-make-flesh-cultist = Сделать цель культистом плоти.

View file

@ -0,0 +1,4 @@
roles-antag-flesh-cultist-name = Культист плоти
roles-antag-flesh-cultist-objective = Оставайтесь в тени подготавливая погибель для станции.
roles-antag-flesh-cultist-leader-name = Лидер культа плоти
roles-antag-flesh-cultist-leader-objective = Воплотите цели культа в реальность. Убедитесь что все участники культа выживут.

View file

@ -0,0 +1,4 @@
store-category-flesh-passive-skills = Улучшения
store-category-flesh-active-skills = Умения
store-category-flesh-weapon = Модификации рук
store-category-flesh-armor = Модификации тела

View file

@ -0,0 +1 @@
store-currency-display-stolen-mutation-points = Очки Эволюции

View file

@ -0,0 +1,23 @@
ent-Flesh = живая плоть
.desc = Оно живое!
.suffix = { "" }
ent-FleshBlade = рука-линок из плоти
.desc = Почти как у генокрада, только лучше.
.suffix = { "" }
ent-FleshClaw = клешня из плоти
.desc = Нею можно вскрывать как двери так и людей.
.suffix = { "" }
ent-ClothingFleshSpiderLegs = паучьи ноги
.desc = 2 ноги хорошо, 8 лучше.
.suffix = { "" }
ent-ClothingOuterArmorFlesh = броня из плоти
.desc = А где кожа?
.suffix = { "" }
ent-FleshSpikeHandGun = рука-шипострел
.desc = Пиу-Пау
.suffix = { "" }
ent-FleshFist = кулак из плоти
.desc = Fisting is three hundred bucks
.suffix = { "" }

View file

@ -0,0 +1,43 @@
flesh-pudge-throw-hugger-name = Выблевать червя
flesh-pudge-throw-hugger-popup = Выблевывает червя
flesh-pudge-throw-hugger-eat-face-user = Червь ест ваше лицо!
flesh-pudge-throw-hugger-bite-user = Червь укусил вас за руку!
flesh-pudge-throw-hugger-try-unequip = Вы не можете снять червя с лица.
flesh-pudge-throw-hugger-eat-face-others = Червь ест лицо { CAPITALIZE($entity) }!
flesh-pudge-throw-hugger-hit-others = К лицу { CAPITALIZE($entity) } присосался червь!
flesh-pudge-throw-hugger-hit-mob = Вы присосались к лицу { CAPITALIZE($entity) }!
flesh-pudge-throw-hugger-hit-user = К вашему лицу присосался червь!
flesh-pudge-throw-hugger-desc = Выблюйте червя из плоти и возьмите его в свою руку для броска во врагов.
flesh-pudge-transform-user = Вы превращаетесь в { CAPITALIZE($EntityTransform) }
flesh-pudge-transform-others = { CAPITALIZE($Entity) } превращается в { CAPITALIZE($EntityTransform) }
flesh-pudge-transform-begin-user = Вы начинаете превращаться.
flesh-pudge-transform-begin-others = { CAPITALIZE($Entity) } начинает превращается.
flesh-mob-cant-atack-flesh-heart = Вы не можете атаковать сердце плоти.
flesh-mob-cant-atack-flesh-mob = Вы не можете атаковать монстров из плоти.
flesh-mob-cant-atack-flesh-cultist = Вы не можете атаковать культистов плоти.
flesh-hugger-jump-name = Прыжок
flesh-hugger-jump-desc = Позволяет вам прыгнуть в выбраном направлении. Если вы попадете в гуманоида без маски вы зацепитесь за его лицо.
flesh-hugger-get-off-name = Слезть с лица
flesh-hugger-get-off-desc = Позволяет вам спуститься с лица.
mob-flesh-ghost-role-rules = Не атакуйте культистов плоти, не ломайте ДАМы, сервера, консоли и прочее. Не делайте разгерметизацию.
mob-flesh-abom-ghost-role-name = Ужасное порождение из плоти
mob-flesh-abom-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
mob-flesh-pudge-ghost-role-name = Толстяк из плоти
mob-flesh-pudge-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
mob-flesh-spider-ghost-role-name = Тарантул из плоти
mob-flesh-spider-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
mob-flesh-ball-ghost-role-name = Мячик из плоти
mob-flesh-ball-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
mob-flesh-bat-ghost-role-name = Летащая мышь из плоти
mob-flesh-bat-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
mob-flesh-worm-ghost-role-name = Червь из плоти
mob-flesh-worm-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
mob-flesh-small-worm-ghost-role-name = Маленьнький червь из плоти
mob-flesh-small-worm-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
mob-flesh-hugger-ghost-role-name = лицехват из плоти
mob-flesh-hugger-ghost-role-decs = Вы разумная плоть, кооперируйтесь с другими существами из плоти для захвата станции.
flesh-worm-cant-get-off = Вы не на лице.
flesh-worm-cant-jump = Вы не можете прыгать будучи на лице.

View file

@ -0,0 +1,5 @@
ent-FleshHeart = сердце плоти
.desc = Такой хуйни вы еще не видели.
flesh-heart-cant-absorb-targer = Сердце не хочет поглощать это.
flesh-heart-activate-warning = Внимание! На станции была замечена аномальная биологическая сигнатура. Всему персоналу отдела службы безопастности начать немедленную ликвидацию цели.

View file

@ -0,0 +1,101 @@
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-user-hand-blocked = Данная рука занята другой модификацией.
flesh-cultist-transform-armor-blocked = Вы не можете использовать паучьи ноги и броню одновременно.
flesh-cultist-transform-spider-legs-blocked = Вы не можете использовать броню с паучьими лапами.
flesh-cultist-infection-target-critical = Вы не можете заразить умирающее существо.
flesh-cultist-infection-target-dead = Вы не можете заразить мертвое существо.
flesh-cultist-infection-target-invalid = Вы не можете заразить данное существо.
flesh-cultist-equiped-outer-clothing-blocked = Вы не можете это одеть.
flesh-cultist-infected-user = Вы чувствуете, что внутри вас что-то развивается.
flesh-cultist-devout-target-not-have-flesh = Вы не можете поглощать существа, не состоящие из плоти.
flesh-cultist-devout-target-alive = Вы не можете поглощать живых существ.
flesh-cultist-devout-target-invalid = Вы не можете поглотить это.
flesh-cultist-devout-not-hungry = Паразит не достаточно голоден, чтобы поглотить это.
flesh-cultist-hungry = Паразит голоден.
flesh-cultist-equipped-outer-clothing-blocked = Вы не можете одеть это, пока у вас есть паучьи ноги.
flesh-cultist-cant-spawn-flesh-heart-in-space = Сердце плоти невозможно создать в космосе.
flesh-cultist-cant-spawn-flesh-heart-here = Слишком мало места для сердца плоти.
flesh-cultist-cant-absorb-puddle = Рядом нету крови.
flesh-cultist-absorb-puddle = { CAPITALIZE($Entity) } поглощает лужу крови.
flesh-cultist-devour-target = { CAPITALIZE($Entity) } поглощает { CAPITALIZE($Target) }.
flesh-cultist-not-find-puddles = Рядом нету никаких луж.
flesh-cultist-throw-hugger = Вы бросата лицехвата.
flesh-cultist-throw-hugger-others = { CAPITALIZE($Entity) } бросает лицехвата.
flesh-cultist-hunger-name = Голод паразита
flesh-cultist-hunger-desc = Когда голод паразита достигнет нуля, он вырвется наружу, и вы потеряете контроль над своим телом.
flesh-cultist-shop-name = Магазин эволюции
flesh-cultist-shop-desc = Здесь вы можете приобрести активные навыки и пассивные улучшения.
flesh-cultist-blade-name = Клинок из плоти
flesh-cultist-blade-desc = Превращает активную руку в смертоносный клинок из плоти и костей.
flesh-cultist-break-cuffs-name = Сброс оков
flesh-cultist-break-cuffs-desc = Вы можете сорвать с себя любые наручники или смирительную рубашку.
flesh-cultist-adrenalin-name = Синтез эпинефрина
flesh-cultist-adrenalin-desc = Паразит синтезирует и вводит вам дозу эпинефрина, что повышает вашу скорость и сопротивление оглушению.
flesh-cultist-claw-name = Клешня из плоти
flesh-cultist-claw-desc = Превращает активную руку в функциональную клешню из плоти и костей.
flesh-cultist-fist-name = Кулак из плоти
flesh-cultist-fist-desc = Превращает активную руку в массивный кулак из плоти которым можно легко ломать любые структуры.
flesh-cultist-spike-gun-name = Рука шипострел
flesh-cultist-spike-gun-desc = Превращает активную руку в смертоносный шипострел из плоти.
flesh-cultist-armor-name = Броня из плоти
flesh-cultist-armor-desc = Облачает вас в броню из плоти и костей.
flesh-cultist-spider-legs-name = Паучий облик
flesh-cultist-spider-legs-desc = Превращает часть вашего тела в паучий вид, давая небольшую защиту и большой прирост скорости.
flesh-cultist-absorb-blood-pool-name = Поглощение лужи крови
flesh-cultist-absorb-blood-pool-desc = Позволяет поглотить чистые лужи крови для лечения.
flesh-cultist-devour-name = Поглощение трупа
flesh-cultist-devour-desc = Вы можете поглотить любое существо из плоти, чтобы получить очки эволюции и исцелить раны.
flesh-cultist-create-flesh-heart-name = Создание сердца из плоти
flesh-cultist-create-flesh-heart-desc = Создает сердце из плоти перед вами. Его создание - ваша ключевая задача на станции. Для активации необходимо использовать 3 тела развитых существ из плоти. После активации будьте готовы защищать его от сотрудников службы безопасности.
flesh-cultist-throw-hugger-name = Бросок лицехвата
flesh-cultist-throw-hugger-desc = Создайте и метните в лица врагов лицехвата из плоти.
flesh-cultist-acid-spit-name = Кислотный плевок
flesh-cultist-acid-spit-desc = Плюйтесь кислотой в ваших врагов
flesh-cultist-blade-evolution-name = Клинок из плоти
flesh-cultist-blade-evolution-desc = Получите возможность превратить свою руку в смертоносный клинок из плоти и костей.
flesh-cultist-claw-evolution-name = Клешня из плоти
flesh-cultist-claw-evolution-desc = Получите возможность превратить свою руку в функциональную клешню из плоти и костей.
flesh-cultist-fist-evolution-name = Кулак из плоти
flesh-cultist-fist-evolution-desc = Получите возможность превратить свою руку в массивный кулак из плоти которым можно легко ломать любые структуры.
flesh-cultist-spike-gun-evolution-name = Рука шипострел
flesh-cultist-spike-gun-evolution-desc = Превратите активную руку в смертоносный шипострел из плоти.
flesh-cultist-medium-armor-evolution-name = Броня из плоти
flesh-cultist-medium-armor-evolution-desc = Получите способность облачать себя в среднюю броню из плоти и костей.
flesh-cultist-heavy-armor-evolution-name = Тяжелая броня из плоти
flesh-cultist-heavy-armor-evolution-desc = Получите способность облачать себя в тяжелую броню из плоти и костей которая дает невероятную защиту и возможность отражать выстрелы но сильно замедляет движение.
flesh-cultist-spider-legs-evolution-name = Паучий облик
flesh-cultist-spider-legs-evolution-desc = Получите возможность превратить часть тела в паука, чтобы получить небольшую защиту и большой прирост скорости.
flesh-cultist-break-cuffs-evolution-name = Сброс оков
flesh-cultist-break-cuffs-evolution-desc = Получите возможность высвободиться от наручников или смирительной рубашки.
flesh-cultist-adrenaline-evolution-name = Синтез адреналина
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-create-flesh-heart-evolution-name = Создание сердца из плоти
flesh-cultist-create-flesh-heart-evolution-desc = Получите возможность создать сердце из плоти.
Его создание и пробуждение - ваша ключевая задача на станции.
Для пробуждения потребуется три тела развитых существ из плоти. После активации будьте готовы защищать его от любой угрозы.
flesh-cultist-insulated-immunity-evolution-name = Сопротивление
flesh-cultist-insulated-immunity-evolution-desc = Вы сможете спокойно работать с электричеством не опасаясь поражения напряжением.
flesh-cultist-pressure-immunity-evolution-name = Уплотнение
flesh-cultist-pressure-immunity-evolution-desc = Вам больше не будет угрожать низкое давление окружающей среды.
flesh-cultist-cold-teml-immunity-evolution-name = Термо-синтез
flesh-cultist-cold-teml-immunity-evolution-desc = Вы сможете поддерживать температуру тела стабильной в условиях холодного космоса.
flesh-cultist-flash-immunity-evolution-name = Защита глаз
flesh-cultist-flash-immunity-evolution-desc = Вас больше не будут ослеплять яркие вспышки.
flesh-cultist-respirator-immunity-evolution-name = Закрытая система дыхания.
flesh-cultist-respirator-immunity-evolution-desc = Вам больше не нужен кислород.

View file

@ -0,0 +1,14 @@
ent-MobFleshSpider = тарантул из плоти
.desc = Очень похоже на паука, только вывернутого наизнанку
ent-MobFleshPudge = толстяк из плоти
.desc = Большой мальчик
ent-MobFleshBall = мячик из плоти
.desc = Лучше не играть ним в футбол
ent-MobFleshBat = летающая мышь из плоти
.desc = Она не дружит с священником
ent-MobFleshWorm = червь из плоти
.desc = Вот что будет внутри вас если есть много конфет
ent-MobSmallFleshWorm = маленький червь из плоти
.desc = { ent-MobFleshWorm.desc }
ent-MobFleshHugger = лицехват из плоти
.desc = Он хочет сесть тебе на лицо

View file

@ -0,0 +1,81 @@
flesh-cult = Культ плоти
flesh-cult-round-end-result =
{ $cultistsCount ->
[one] Был один культист плоти.
*[other] Было { $cultistsCount } культистов плоти.
}
flesh-cult-and-traitor-title = Культ плоти и Предатели
flesh-cult-and-traitor-description = На станции одновременно действует культ плоти и предатели.
flesh-cult-round-end-agent-name = культист плоти
flesh-cult-round-end-cultist-live-amount-none = [color=green]Все культисты были уничтожены![/color]
flesh-cult-round-end-cultist-live-amount-low = [color=green]Почти все культисты были уничтожены.[/color]
flesh-cult-round-end-flesh-heart-succes = [bold][color=red]Станция была обращена в обитель плоти![/color][/bold]
flesh-cult-round-end-flesh-heart-fail = [bold][color=green]Культу плоти не удалось выполнить свою цель![/color][/bold]
# Shown at the end of a round of Traitor
flesh-cult-user-was-a-cultist = [color=gray]{ $user }[/color] был(а) культистом плоти.
flesh-cult-user-was-a-cultist-named = [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) был(а) культистом плоти.
flesh-cult-was-a-traitor-named = [color=White]{ $name }[/color] был(а) культистом плоти.
flesh-cult-user-was-a-cultist-leader = [color=gray]{ $user }[/color] был(а) лидером культа плоти.
flesh-cult-user-was-a-cultist-leader-named = [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) был(а) лидером культа плоти.
flesh-cult-was-a-cultist-leader-named = [color=White]{ $name }[/color] был(а) лидером культа плоти.
flesh-cult-user-was-a-cultist-with-objectives = [color=gray]{ $user }[/color] был(а) культистом плоти со следующими целями:
flesh-cult-user-was-a-cultist-with-objectives-named = [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) был(а) культистом плоти со следующими целями:
flesh-cult-was-a-cultist-with-objectives-named = [color=White]{ $name }[/color] был(а) культистом плоти со следующими целями:
flesh-cult-user-was-a-cultist-leader-with-objectives = [color=gray]{ $user }[/color] был(а) лидером культа плоти со следующими целями:
flesh-cult-user-was-a-cultist-leader-with-objectives-named = [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) был(а) лидером культа плоти со следующими целями:
flesh-cult-was-a-cultist-leader-with-objectives-named = [color=White]{ $name }[/color] был(а) лидером культа плоти со следующими целями:
flesh-cult-round-end-leader = [bold]Лидером культа плоти был [color=White]{ $name }[/color] ([color=gray]{ $username }[/color])[/bold]
preset-flesh-cult-objective-issuer-flesh-cult = [color=#e0106a]Культ плоти[/color]
objective-issuer-flesh-cult = [color=#e0106a]Культ плоти[/color]
# Shown at the end of a round of Traitor
flesh-cult-objective-condition-success = { $condition } | [color={ $markupColor }]Успех![/color]
# Shown at the end of a round of Traitor
flesh-cult-objective-condition-fail = { $condition } | [color={ $markupColor }]Провал![/color] ({ $progress }%)
flesh-cult-title = Культ плоти
flesh-cult-description = На станции появился культ, который желает захватить станцию.
flesh-cult-not-enough-ready-players = Недостаточно игроков готовы к игре! Из { $minimumPlayers } необходимых игроков готовы { $readyPlayersCount }.
flesh-cult-no-one-ready = Нет готовых игроков! Не удалось начать режим Культа плоти.
# TraitorRole
flesh-cult-role-greeting =
Вы - участник культа плоти.
Ваши цели и соратники указаны в меню персонажа.
Поедайте существ из плоти чтобы получить новые навыки, оставайтесь в тени и готовите пришествие плоти на станцию.
flesh-cult-role-cult-members =
Ваши соратники:
{ $cultMembers }
Не позволяйте им умереть напрасно.
# TraitorRole
flesh-cult-role-greeting-leader =
Вы - лидер культа плоти.
Ваши цели и соратники указаны в меню персонажа.
Поедайте существ из плоти чтобы получить новые навыки, оставайтесь в тени и готовите пришествие плоти на станцию.
Убедитесь что никто из участников вашего культа не погибнет напрасно.
flesh-cult-round-end-count-create-flesh-hearts =
{ $heartsCount ->
[one] Было создано одно сердце плоти.
*[other] Было создано { $heartsCount } сердец плоти.
}
flesh-cult-round-end-count-activate-flesh-hearts =
{ $heartsCount ->
[one] Было пробуждено одно сердце плоти.
*[other] Было пробуждено { $heartsCount } сердец плоти.
}
flesh-cult-round-end-count-no-activate-flesh-hearts = Ни одно сердце плоти не было пробуждено.
flesh-cult-round-end-count-destroy-flesh-hearts =
{ $heartsCount ->
[one] Было уничтожено одно сердце плоти.
*[other] Было уничтожено { $heartsCount } сердец плоти.
}
flesh-cult-round-end-count-no-destroy-flesh-hearts = Ни одно сердце плоти не было уничтожено.

View file

@ -0,0 +1,4 @@
reagent-name-carol = ??????
reagent-desc-carol = ??????
reagent-name-flesh-acid = ??????
reagent-desc-flesh-acid = ??????

View file

@ -455,22 +455,22 @@ station-goal-training-crew =
=============================================
⠀Приветствую, уважаемое командование станции!
⠀Цель вашей текущей смены - [bold]ОБУЧЕНИЕ ЭКИПАЖА ПОСРЕДСТВОМ ОТКРЫТЫХ ЛЕКЦИЙ[/bold]
⠀Каждый отдел должен выбрать [bold]одного или двух ораторов[/bold] и подготовить свой план лекции отдела, который должен быть заверен печатью главы,
⠀Каждый отдел должен выбрать [bold]одного или двух ораторов[/bold] и подготовить свой план лекции отдела, который должен быть заверен печатью главы,
а после провести эту лекцию публично.
Условия:[bold]
1. Допускается проведение лекции как в отделе, так и на отдельно подготовленной сцене.
2. Лекция считается выполненной успешно, если на ней было обучено минимум 10 членов ДРУГИХ отделов.
3. Цель считается выполненной, если было успешно проведено минимум 3 лекции с подготовленными о них отчётами, в которых должно указываться:
- Количество пришедших;
- Количество обученных;
- Какие навыки отдела затрагивает лекция;
- Имена ораторов;
3. Цель считается выполненной, если было успешно проведено минимум 3 лекции с подготовленными о них отчётами, в которых должно указываться:
- Количество пришедших;
- Количество обученных;
- Какие навыки отдела затрагивает лекция;
- Имена ораторов;
- Место проведения;[/bold]
Всё отчёты должны быть доставлены на эвакуационном шаттле.
Ответственные за цель: [bold]ИНЖЕНЕРНЫЙ ОТДЕЛ, НАУЧНЫЙ ОТДЕЛ, ОТДЕЛ СНАБЖЕНИЯ, СЕРВИСНЫЙ ОТДЕЛ, ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ,
Ответственные за цель: [bold]ИНЖЕНЕРНЫЙ ОТДЕЛ, НАУЧНЫЙ ОТДЕЛ, ОТДЕЛ СНАБЖЕНИЯ, СЕРВИСНЫЙ ОТДЕЛ, ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ,
МЕДИЦИНСКИЙ ОТДЕЛ, КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
[italic]Место для печатей[/italic]
@ -492,11 +492,11 @@ station-goal-creation-of-furs =
3. Мех модели "Дюранд" (1 шт.).
4. Каждому меху по 2 запасных энергоячейки. [/bold]
Отделу снабжения даётся задача заказать и добыть все необходимые материалы для создания мехов.
Службе безопасности в обязательном порядке даётся задача проконтролировать доставку ВСЕХ созданных из списка мехов и
Службе безопасности в обязательном порядке даётся задача проконтролировать доставку ВСЕХ созданных из списка мехов и
запасных энергоячеек на эвакуационном шаттле.
Ответственные за цель: [bold]НАУЧНЫЙ ОТДЕЛ, ОТДЕЛ СНАБЖЕНИЯ, ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ, КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
[italic]Место для печатей[/italic]
@ -517,15 +517,15 @@ station-goal-saboteurs =
Условия:
1. Выбранный офицер не должен нарушать статьи корпоративного закона выше 4XX, статьи с кодом 3 и 7 нарушать [bold]СТРОГО[/bold] запрещено.
2. Выбранный офицер имеет право оказывать любое [bold]нелетальное[/bold] сопротивление ловящим его сотрудникам службы безопасности.
3. В случае подтверждения угрозы на станции и введение красного кода, учения должны быть немедленно остановлены,
3. В случае подтверждения угрозы на станции и введение красного кода, учения должны быть немедленно остановлены,
диверсанты обязаны оказать помощь отделу в устранении угрозы. После ликвидации, учения должны породолжаться в прежнем режиме.
Задача отдела безопасности найти "нарушителя" и в соответствии с процедурами ареста, КЗ и СРП провести его задержание, допрос и вынесение приговора.
После этого рекрутированный офицер должен быть освобождён. ГСБ должен составить отчёт о действиях диверсанта и работе СБ.
В случае, если диверсант так и не был установлен и арестован, учения заканчиваются. ГСБ так же должен составить отчёт о действиях диверсанта и
После этого рекрутированный офицер должен быть освобождён. ГСБ должен составить отчёт о действиях диверсанта и работе СБ.
В случае, если диверсант так и не был установлен и арестован, учения заканчиваются. ГСБ так же должен составить отчёт о действиях диверсанта и
работе СБ.
Ответственные за цель: [bold]ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ, КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
[italic]Место для печатей[/italic]
@ -539,7 +539,7 @@ station-goal-reconnaissance-in-force =
ЦЕЛЬ СМЕНЫ { $station }
=============================================
⠀Приветствую, уважаемое командование станции!
⠀Цель вашей смены - провести 3 успешных полётов на планеты с уровнем сложности "высокая" и подготовить отчёт о каждом вылете
⠀Цель вашей смены - провести 3 успешных полётов на планеты с уровнем сложности "высокая" и подготовить отчёт о каждом вылете
в соответствии с формой "отчёт об экспедиции".
⠀Руководство недовольно потерей ряда объектов в вашем секторе, по этому необходимо направить экспедиционные команды для расследования этих инцидентов.
⠀Необходимо собрать отряд добровольцев, который будет включать в себя:[bold]
@ -550,7 +550,7 @@ station-goal-reconnaissance-in-force =
⠀При возникновений сложностей с фауной, вы имеете право запросить огневую поддержку у сил службы безопасности вашей станции.
⠀Ответственные за цель: [bold]НАУЧНЫЙ ОТДЕЛ, МЕДИЦИНСКИЙ ОТДЕЛ, ОТДЕЛ СНАБЖЕНИЯ, КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
[italic]Место для печатей[/italic]
@ -575,10 +575,10 @@ station-goal-reports =
-Подготовить Отчёт о одобренных заказах.
-Подготовить Отчёт об исследованиях артефактов.
-Подготовить Отчёт об использованных за смену препаратах.[/bold]
На выполнение данной задачи у вас 2 часа, после ПЦК может известить о своем прибытии для личной проверки всех отчётов.
На выполнение данной задачи у вас 2 часа, после ПЦК может известить о своем прибытии для личной проверки всех отчётов.
В противном случае все отчёты должны быть доставлены на СЦК
Ответственные за цель: [bold]ОТДЕЛ СНАБЖЕНИЯ, НАУЧНЫЙ ОТДЕЛ, МЕДИЦИНСКИЙ ОТДЕЛ, ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ,
Ответственные за цель: [bold]ОТДЕЛ СНАБЖЕНИЯ, НАУЧНЫЙ ОТДЕЛ, МЕДИЦИНСКИЙ ОТДЕЛ, ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ,
СЕРВИСНЫЙ ОТДЕЛ, КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
@ -596,21 +596,21 @@ station-goal-intelligent-weapons =
⠀Приветствую, уважаемое командование станции!
⠀Цель вашей текущей смены - [bold]СОЗДАНИЕ РАЗУМНОГО ОРУЖИЯ[/bold]
Было принято решение о создании нового вида оружия для защиты наших колоний. Для выполения цели потребуется:[bold]
1. Отделу снабжения необходимо привезти туши ксеноморфов двух видов, а также заказать все необходимые ресурсы для постройки
1. Отделу снабжения необходимо привезти туши ксеноморфов двух видов, а также заказать все необходимые ресурсы для постройки
камер содержания в ксеноархеологии для них.
2. Медицинскому отделу необходимо реанимировать туши ксеноморфов, а также ввести в каждого из них когнизин и
2. Медицинскому отделу необходимо реанимировать туши ксеноморфов, а также ввести в каждого из них когнизин и
поддерживать здоровое состояние каждой особи до окончания смены.
3. Научному отделу необходимо изучить способности, степень работоспособности каждой особи, а также заставить каждую особь повиноваться и слушать вас.
4. Инженерному отделу даётся задача не просто восстановить ксеноархеологию а укрепить и поддерживать ее целостность на протяжении всей смены.
5. Службе безопасности даётся задача проконтролировать содержание существ, не давая им выйти из зоны содержания, на протяжении всей смены. [/bold]
По окончанию выполенения цели необходимо доставить особей на СЦК.
По окончанию выполенения цели необходимо доставить особей на СЦК.
В случае, если особи окажутся невосприимчивы к препарату, они должны быть уничтожены.
В случае побега особей из камеры содержания, из-за недостатка её прочности, ответственные сотрудники инженерного отдела должны быть
задержаны по статье 402 КЗ а особи найдены и ликвидированы.
В случае побега особей из камеры содержания, из-за недостатка её прочности, ответственные сотрудники инженерного отдела должны быть
задержаны по статье 402 КЗ а особи найдены и ликвидированы.
Ответственные за цель: [bold]ОТДЕЛ СНАБЖЕНИЯ, НАУЧНЫЙ ОТДЕЛ, МЕДИЦИНСКИЙ ОТДЕЛ, ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ, ИНЖЕНЕРНЫЙ ОТДЕЛ,
Ответственные за цель: [bold]ОТДЕЛ СНАБЖЕНИЯ, НАУЧНЫЙ ОТДЕЛ, МЕДИЦИНСКИЙ ОТДЕЛ, ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ, ИНЖЕНЕРНЫЙ ОТДЕЛ,
КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
[italic]Место для печатей[/italic]
@ -624,8 +624,8 @@ station-goal-medical-replenishment =
ЦЕЛЬ СМЕНЫ { $station }
=============================================
⠀Приветствую, уважаемое командование станции!
⠀Цель вашей текущей смены - [bold]ПРОИЗВОДСТВО ПОПОЛНЕНИЯ ЛЕКАРСТВ ДЛЯ СЦК [/bold]
Одна из наших колоний слишком сильно пострадала от нападения ксеноморфов. В данный момент колония не может нормально функционировать и
⠀Цель вашей текущей смены - [bold]ПРОИЗВОДСТВО ПОПОЛНЕНИЯ ЛЕКАРСТВ ДЛЯ СЦК [/bold]
Одна из наших колоний слишком сильно пострадала от нападения ксеноморфов. В данный момент колония не может нормально функционировать и
все раненные были перемещены на СЦК, потому медицинскому отделу даётся задача произвести следующий список лекарств:[bold]
1. Сигинат (200 унций).
2. Дексалин плюс (200 унций).
@ -656,12 +656,12 @@ station-goal-medical-replenishment =
6. Набор для выведения радиации (2 шт.).
7. Коробка мешков для тел (6 шт.).[/bold]
ЦК заботится о своих сотрудниках поэтому запрещает приступать к цели пока не выполнен
ЦК заботится о своих сотрудниках поэтому запрещает приступать к цели пока не выполнен
"заказ изготовление основных препаратов для оказания медицинской помощи".
Все препараты и наборы должны быть аккуратно собраны в ящики и доставленны на СЦК.
Ответственные за цель: [bold]МЕДИЦИНСКИЙ ОТДЕЛ, СЕРВИСНЫЙ ОТДЕЛ, КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
[italic]Место для печатей[/italic]
station-goal-anomaly =
@ -677,11 +677,11 @@ station-goal-anomaly =
⠀Цель вашей текущей смены - [bold]ГЕНЕРАЦИЯ АНОМАЛИЙ[/bold].
Корпорация планирует продвинуться в изучении аномальных объектов и РедСпейса в частности.
Научному отдела ставится задача сгенерировать или найти на станции [bold]4 аномалии разного типа[/bold],
осуществить подробное изучение объектов и составить на основе полученных данных отчёты
с отправкой энных на ЦентКом.
Научный отдел вправе привлекать другие отделы для выполнения цели.
Заказы учёных в Карго получают приоритет и должны быть рассмотрены в кратчайшие сроки,
при условии, что они требуются для успешного выполнения задачи.
осуществить подробное изучение объектов и составить на основе полученных данных отчёты
с отправкой энных на ЦентКом.
Научный отдел вправе привлекать другие отделы для выполнения цели.
Заказы учёных в Карго получают приоритет и должны быть рассмотрены в кратчайшие сроки,
при условии, что они требуются для успешного выполнения задачи.
Инженерный отдел может помогать аномалистам в создании благоприятных условий для сдерживания объектов.
В условии к оформлению отчёта может быть указано:
- Внешнее описание аномальных объектов;
@ -702,7 +702,7 @@ station-goal-testing-sb =
=============================================
⠀Приветствую, уважаемое командование станции!
⠀Цель вашей текущей смены - [bold]ТЕСТИРОВАНИЕ ДЛЯ ВСЕГО ОТДЕЛА СЛУЖБЫ БЕЗОПАСНОСТИ[/bold].
В последнее время от многих станций стало поступать огромное количество жалоб на работу службы безопасности,
В последнее время от многих станций стало поступать огромное количество жалоб на работу службы безопасности,
в связи с этим нами было принято решение провести повторное тестирование службы безопасности на каждой станции!
Для этого вам потребуется:[bold]
- Организовать Тестирование СБ по СРП, КЗ и всяческим процедурам.
@ -711,10 +711,10 @@ station-goal-testing-sb =
- Подготовить полосу с препятствиями ввиде луж, стен, ограждений и т.п.
- Произвести оценку по навыкам управления и стрельбы из мехов службы безопасности.
- Произвести Учения, по работе при ЧС.[/bold]
Для выполнения цели отдел службы безопасности вправе попросить помощи у других отделов для организации тестирования
По завершении цели отослать отчёты с показателем каждого в СБ, включая ГСБ.
⠀Ответственные за цель: [bold]ОТДЕЛ СЛУЖБЫ БЕЗОПАСНОСТИ, КАПИТАН СТАНЦИИ { $station }[/bold].
=============================================
@ -755,10 +755,10 @@ station-goal-delegates =
=============================================
⠀Приветствую, уважаемое командование станции!
⠀Цель вашей смены - [bold]ПОДГОТОВИТЬ СТАНЦИЮ К ПРИНЯТИЮ ДЕЛЕГАТОВ[/bold].
В связи с недавними событиями различные корпорации и государства в том числе и NanoTrasen решили провести переговоры для состовления
В связи с недавними событиями различные корпорации и государства в том числе и NanoTrasen решили провести переговоры для состовления
плана сотрудничества на ближайшее время, поэтому нами было принято решение выбрать вашу станцию для проведения переговоров.
Сервисному отделу даётся задача привести и поддерживать на станции чистоту вплоть до конца смены, заменить лампы, подготовить по 10 сложных блюд и
Сервисному отделу даётся задача привести и поддерживать на станции чистоту вплоть до конца смены, заменить лампы, подготовить по 10 сложных блюд и
напитков (в шейкерах) и разместить их в холодильнике.
Научному отделу даётся задача модифицировать отделы, добавив по 8-10 нового оборудования в каждый отдел и изучив хотя бы одну ветку изучений до третьего уровня
@ -774,3 +774,28 @@ station-goal-delegates =
=============================================
[italic]Место для печатей[/italic]
station-goal-virus =
║[color=#1b487e]███╗░░██╗████████╗[/color]
║[color=#1b487e]████╗░██║╚══██╔══╝[/color] [head=3]Цель смены[/head]
║[color=#1b487e]██╔██╗██║░░░██║░░░[/color] [head=3]NanoTrasen[/head]
║[color=#1b487e]██║╚████║░░░██║░░░[/color]
║[color=#1b487e]██║░╚███║░░░██║░░░[/color]
║[color=#1b487e]╚═╝░░╚══╝░░░╚═╝░░░[/color]
║═════════════════════════════════════════
ЦЕЛЬ СМЕНЫ
║═════════════════════════════════════════
Уважаемый Капитан Станции. Вашей смене поставлена совершенно секретная цель. Вы, получив и прочитав данный документ, соглашаетесь с политикой неразглашения. Вам позволительно разглашать детали этой цели Командному составу, как и необходимым сотрудникам, которые будут исполнять поставленную задачу, но будьте бдительны, СМИ не должны заполучить данный документ или узнать об этом эксперименте.
1. Необходимо построить укреплённую камеру из армированного стекла для проведения опытов вблизи РНД, минимальный размер внутренней части должен быть не меньше 2x2 метра, вокруг оградить решеткой, запитанную электричеством. В зону проведения опытов должна быть проведена труба, которая, в случае неудачи, сможет подать газ в камеру, тем самым убив всё содержимое.
2. Вам необходимо найти двух, но не более четырёх добровольцев (не обязательно говорить им об опасности и их дальнейшей смерти). Ими могут быть пассажиры, обычные сотрудники станции или заключенные, включая приговорённых к смертной казни.
3. В вашем шкафу находится шприц с неизвестным вирусом, этот химический состав вы должны ввести в испытуемых в построенной камере.
3.1. Исследовать рацион заражённых.
3.2. Исследовать естественное распространение вируса на другое живое существо - это может быть как член экипажа, так и животное.
3.3. Исследовать возможную коммуникацию с данными существами.
4. Эксперимент должен письменно фиксироваться, а документ о ходе эксперимента нужно будет предоставить Центральному Командованию. После дождаться прибытия Представителя Центрального Командования, или разрешения процедуры эвакуации.
Слава NanoTrasen!
║═════════════════════════════════════════
║ [italic]Место для печатей[/italic]

View file

@ -135,6 +135,7 @@
- type: Tag
tags:
- Window
- Directional # Sunrise-Edit
- type: MeleeSound
soundGroups:
Brute:
@ -284,4 +285,4 @@
sprite: Structures/Windows/cracks_diagonal.rsi
- type: Construction
graph: WindowDiagonal
node: windowDiagonal
node: windowDiagonal

View file

@ -24,6 +24,7 @@
- id: SpiderClownSpawn
- id: SpiderSpawn
- id: VentClog
- id: VentFleshWorms
- type: entityTable
id: BasicAntagEventsTable

View file

@ -12,3 +12,18 @@
- type: Injector
injectOnly: false
toggleState: Inject
- type: entity
parent: BaseSyringe
id: SyringeCarolNT
components:
- type: SolutionContainerManager
solutions:
injector:
maxVol: 15
reagents:
- ReagentId: Carol
Quantity: 5
- type: Injector
injectOnly: false
toggleState: Inject

View file

@ -0,0 +1,182 @@
- type: entity
id: FleshCultistShop
name: flesh-cultist-shop-name
description: flesh-cultist-shop-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistShop.png
event: !type:FleshCultistShopActionEvent
checkCanInteract: false
- type: entity
id: FleshCultistBlade
name: flesh-cultist-blade-name
description: flesh-cultist-blade-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistBlade.png
event: !type:FleshCultistBladeActionEvent
itemIconStyle: NoItem
useDelay: 10
checkCanInteract: false
- type: entity
id: FleshCultistAdrenalin
name: flesh-cultist-adrenalin-name
description: flesh-cultist-adrenalin-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistAdrenalin.png
event: !type:FleshCultistAdrenalinActionEvent
itemIconStyle: NoItem
useDelay: 180
checkCanInteract: false
- type: entity
id: FleshCultistCreateFleshHeart
name: flesh-cultist-create-flesh-heart-name
description: flesh-cultist-create-flesh-heart-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistFleshHeart.png
event: !type:FleshCultistCreateFleshHeartActionEvent
itemIconStyle: NoItem
useDelay: 360
- type: entity
id: FleshCultistClaw
name: flesh-cultist-claw-name
description: flesh-cultist-claw-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistClaw.png
event: !type:FleshCultistClawActionEvent
itemIconStyle: NoItem
useDelay: 10
checkCanInteract: false
- type: entity
id: FleshCultistThrowHugger
name: flesh-cultist-throw-hugger-name
description: flesh-cultist-throw-hugger-desc
categories: [ HideSpawnMenu ]
components:
- type: WorldTargetAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshThrowHugger.png
itemIconStyle: NoItem
event: !type:FleshCultistThrowHuggerActionEvent
range: 200
useDelay: 240
checkCanInteract: false
- type: entity
id: FleshCultistAcidSpit
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
event: !type:FleshCultistAcidSpitActionEvent
range: 200
useDelay: 60
checkCanInteract: false
- type: entity
id: FleshCultistFist
name: flesh-cultist-fist-name
description: flesh-cultist-fist-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistFist.png
event: !type:FleshCultistFistActionEvent
itemIconStyle: NoItem
useDelay: 10
checkCanInteract: false
- type: entity
id: FleshCultistSpikeGun
name: flesh-cultist-spike-gun-name
description: flesh-cultist-spike-gun-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistSpikeGun.png
event: !type:FleshCultistSpikeHandGunActionEvent
itemIconStyle: NoItem
useDelay: 10
checkCanInteract: false
- type: entity
id: FleshCultistArmor
name: flesh-cultist-armor-name
description: flesh-cultist-armor-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistArmor.png
event: !type:FleshCultistArmorActionEvent
itemIconStyle: NoItem
useDelay: 30
checkCanInteract: false
- type: entity
id: FleshCultistHeavyArmor
name: flesh-cultist-heavy-armor-name
description: flesh-cultist-heavy-armor-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistHeavyArmor.png
event: !type:FleshCultistHeavyArmorActionEvent
itemIconStyle: NoItem
useDelay: 30
checkCanInteract: false
- type: entity
id: FleshCultistSpiderlegs
name: flesh-cultist-spider-legs-name
description: flesh-cultist-spider-legs-desc
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistSpiderLegs.png
event: !type:FleshCultistSpiderLegsActionEvent
itemIconStyle: NoItem
useDelay: 30
checkCanInteract: false
- type: entity
id: FleshCultistAbsorbBloodPool
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:FleshCultistAbsorbBloodPoolActionEvent
useDelay: 30
checkCanInteract: false
- type: entity
id: FleshCultistDevour
name: flesh-cultist-devour-name
description: flesh-cultist-devour-desc
categories: [ HideSpawnMenu ]
components:
- type: EntityTargetAction
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistDevour.png
itemIconStyle: NoItem
event: !type:FleshCultistDevourActionEvent
checkCanInteract: false
useDelay: 5
whitelist:
components:
- MobState

View file

@ -0,0 +1,25 @@
- type: entity
id: FleshHuggerJump
name: flesh-hugger-jump-name
description: flesh-hugger-jump-description
categories: [ HideSpawnMenu ]
components:
- type: WorldTargetAction
useDelay: 3
icon: _Sunrise/FleshCult/Interface/Actions/fleshThrowHugger.png
itemIconStyle: NoItem
checkCanAccess: false
range: 200
event: !type:FleshHuggerJumpActionEvent
- type: entity
id: FleshHuggerGetOff
name: flesh-hugger-get-off-name
description: flesh-hugger-get-off-description
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
useDelay: 5
icon: _Sunrise/FleshCult/Interface/Actions/fleshHuggerGetOff.png
itemIconStyle: NoItem
event: !type:FleshHuggerGetOffFromFaceActionEvent

View file

@ -0,0 +1,38 @@
- 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

View file

@ -0,0 +1,10 @@
- type: entity
id: FleshSpiderWebAction
name: spider-web-action-name
description: spider-web-action-description
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon: _Sunrise/FleshCult/Interface/Actions/flesh_web.png
event: !type:SpiderWebActionEvent
useDelay: 30

View file

@ -0,0 +1,217 @@
- type: listing
id: FleshCultistBlade
name: flesh-cultist-blade-evolution-name
description: flesh-cultist-blade-evolution-desc
productAction: FleshCultistBlade
cost:
StolenMutationPoint: 45
categories:
- FleshCultistWeapon
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistInsulatedImmunityMutationEvent
name: flesh-cultist-insulated-immunity-evolution-name
description: flesh-cultist-insulated-immunity-evolution-desc
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistInsulatedImmunityMutation.png
productEvent: !type:FleshCultistInsulatedImmunityMutationEvent
raiseProductEventOnUser: true
cost:
StolenMutationPoint: 10
categories:
- FleshCultistPassiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistPressureImmunityMutationEvent
name: flesh-cultist-pressure-immunity-evolution-name
description: flesh-cultist-pressure-immunity-evolution-desc
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistPressureImmunityMutation.png
productEvent: !type:FleshCultistPressureImmunityMutationEvent
raiseProductEventOnUser: true
cost:
StolenMutationPoint: 5
categories:
- FleshCultistPassiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistColdTempImmunityMutationEvent
name: flesh-cultist-cold-teml-immunity-evolution-name
description: flesh-cultist-cold-teml-immunity-evolution-desc
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistColdTempImmunityMutation.png
productEvent: !type:FleshCultistColdTempImmunityMutationEvent
raiseProductEventOnUser: true
cost:
StolenMutationPoint: 5
categories:
- FleshCultistPassiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistFlashImmunityMutationEvent
name: flesh-cultist-flash-immunity-evolution-name
description: flesh-cultist-flash-immunity-evolution-desc
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistFlashImmunityMutation.png
productEvent: !type:FleshCultistFlashImmunityMutationEvent
raiseProductEventOnUser: true
cost:
StolenMutationPoint: 10
categories:
- FleshCultistPassiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistRespiratorImmunityMutationEvent
name: flesh-cultist-respirator-immunity-evolution-name
description: flesh-cultist-respirator-immunity-evolution-desc
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistRespiratorImmunityMutation.png
productEvent: !type:FleshCultistRespiratorImmunityMutationEvent
raiseProductEventOnUser: true
cost:
StolenMutationPoint: 5
categories:
- FleshCultistPassiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistClaw
name: flesh-cultist-claw-evolution-name
description: flesh-cultist-claw-evolution-desc
productAction: FleshCultistClaw
cost:
StolenMutationPoint: 10
categories:
- FleshCultistWeapon
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistFist
name: flesh-cultist-fist-evolution-name
description: flesh-cultist-fist-evolution-desc
productAction: FleshCultistFist
cost:
StolenMutationPoint: 25
categories:
- FleshCultistWeapon
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistSpikeGun
name: flesh-cultist-spike-gun-evolution-name
description: flesh-cultist-spike-gun-evolution-desc
productAction: FleshCultistSpikeGun
cost:
StolenMutationPoint: 35
categories:
- FleshCultistWeapon
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistMediumArmor
name: flesh-cultist-medium-armor-evolution-name
description: flesh-cultist-medium-armor-evolution-desc
productAction: FleshCultistArmor
cost:
StolenMutationPoint: 25
categories:
- FleshCultistArmor
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistHeavyArmor
name: flesh-cultist-heavy-armor-evolution-name
description: flesh-cultist-heavy-armor-evolution-desc
productAction: FleshCultistHeavyArmor
cost:
StolenMutationPoint: 45
categories:
- FleshCultistArmor
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistSpiderlegs
name: flesh-cultist-spider-legs-evolution-name
description: flesh-cultist-spider-legs-evolution-desc
productAction: FleshCultistSpiderlegs
cost:
StolenMutationPoint: 20
categories:
- FleshCultistArmor
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistAdrenalin
name: flesh-cultist-adrenaline-evolution-name
description: flesh-cultist-adrenaline-evolution-desc
productAction: FleshCultistAdrenalin
cost:
StolenMutationPoint: 30
categories:
- FleshCultistActiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistCreateFleshHeart
name: flesh-cultist-create-flesh-heart-evolution-name
description: flesh-cultist-create-flesh-heart-evolution-desc
productAction: FleshCultistCreateFleshHeart
cost:
StolenMutationPoint: 50
categories:
- FleshCultistActiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistThrowHugger
name: flesh-cultist-throw-hugger-evolution-name
description: flesh-cultist-throw-hugger-evolution-desc
productAction: FleshCultistThrowHugger
cost:
StolenMutationPoint: 30
categories:
- FleshCultistActiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: FleshCultistAcidSpit
name: flesh-cultist-acid-spit-evolution-name
description: flesh-cultist-acid-spit-evolution-desc
productAction: FleshCultistAcidSpit
cost:
StolenMutationPoint: 30
categories:
- FleshCultistActiveSkills
conditions:
- !type:ListingLimitedStockCondition
stock: 1

View file

@ -0,0 +1,15 @@
- type: storeCategory
id: FleshCultistPassiveSkills
name: store-category-flesh-passive-skills
- type: storeCategory
id: FleshCultistActiveSkills
name: store-category-flesh-active-skills
- type: storeCategory
id: FleshCultistWeapon
name: store-category-flesh-weapon
- type: storeCategory
id: FleshCultistArmor
name: store-category-flesh-armor

View file

@ -0,0 +1,4 @@
- type: currency
id: StolenMutationPoint
displayName: store-currency-display-stolen-mutation-points
canWithdraw: false

View file

@ -0,0 +1,17 @@
- type: npcFaction
id: Flesh
hostile:
- NanoTrasen
- Syndicate
- Xeno
- SimpleHostile
- Carps
- Zombie
- Revolutionary
- PetsNT
- Vampire
- Changeling
- Thief
- type: npcFaction
id: FleshHuman

View file

@ -0,0 +1,41 @@
- type: alert
id: MutationPoint
icons:
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point0
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point1
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point2
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point3
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point4
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point5
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point6
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point7
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point8
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point9
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point10
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point11
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point12
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point13
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point14
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point15
- sprite: /Textures/_Sunrise/FleshCult/Interface/Alerts/flesh_point.rsi
state: flesh_point16
name: flesh-cultist-hunger-name
description: flesh-cultist-hunger-desc
minSeverity: 0
maxSeverity: 16

View file

@ -0,0 +1,19 @@
- type: antag
id: FleshCultist
name: roles-antag-flesh-cultist-name
antagonist: true
setPreference: true
objective: roles-antag-flesh-cultist-objective
requirements:
- !type:OverallPlaytimeRequirement
time: 86400 # 24h # Sunrise-Roletime
- type: antag
id: FleshCultistLeader
name: roles-antag-flesh-cultist-leader-name
antagonist: true
setPreference: true
objective: roles-antag-flesh-cultist-leader-objective
requirements:
- !type:OverallPlaytimeRequirement
time: 86400 # 24h # Sunrise-Roletime

View file

@ -0,0 +1,113 @@
- type: entity
categories: [ HideSpawnMenu ]
parent: ClothingShoesBase
id: ClothingFleshSpiderLegs
name: Flesh Spider Legs
description: "Flesh Spider Legs."
suffix: Flesh Cult
components:
- type: ClothingSpeedModifier
walkModifier: 1.1
sprintModifier: 1.15
- type: Sprite
sprite: _Sunrise/FleshCult/FleshBodyMods/flesh_spider.rsi
- type: Clothing
sprite: _Sunrise/FleshCult/FleshBodyMods/flesh_spider.rsi
- type: Unremoveable
deleteOnDrop: true
- type: NoSlip
- type: FootstepModifier
footstepSoundCollection:
collection: FootstepSpiderLegs
params:
volume: 10
- type: HideLayerClothing
slots:
- RFoot
- LFoot
- RLeg
- LLeg
- type: entity
categories: [ HideSpawnMenu ]
parent: ClothingOuterBaseLarge
id: ClothingOuterArmorFlesh
name: flesh armor
description: flesh armor
suffix: Flesh Cult
components:
- type: Unremoveable
deleteOnDrop: true
- type: Sprite
sprite: _Sunrise/FleshCult/FleshBodyMods/flesh_armor.rsi
- type: Clothing
sprite: _Sunrise/FleshCult/FleshBodyMods/flesh_armor.rsi
- type: Armor
modifiers:
coefficients:
Blunt: 0.5
Slash: 0.5
Piercing: 0.2
Heat: 0.8
- type: ExplosionResistance
damageCoefficient: 0.9
- type: GroupExamine
- type: entity
categories: [ HideSpawnMenu ]
parent: ClothingOuterHardsuitBase
id: ClothingOuterHeavyArmorFlesh
name: heavy flesh armor
description: heavy flesh armor
suffix: Flesh Cult
components:
- type: Sprite
sprite: _Sunrise/FleshCult/FleshBodyMods/heavy_flesh_armor.rsi
- type: Clothing
sprite: _Sunrise/FleshCult/FleshBodyMods/heavy_flesh_armor.rsi
- type: PressureProtection
highPressureMultiplier: 0.2
lowPressureMultiplier: 1000
- type: ExplosionResistance
damageCoefficient: 0.5
- type: Armor
modifiers:
coefficients:
Blunt: 0.6
Slash: 0.6
Piercing: 0.3
Heat: 0.9
- type: ClothingSpeedModifier
walkModifier: 0.8
sprintModifier: 0.8
- type: Tag
tags:
- FullBodyOuter
- type: Unremoveable
deleteOnDrop: true
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetHeavyArmorFlesh
- type: entity
parent: ClothingHeadHardsuitBase
id: ClothingHeadHelmetHeavyArmorFlesh
categories: [ HideSpawnMenu ]
name: heavy flesh helmet
description: heavy flesh helmet
suffix: Flesh Cult
components:
- type: Sprite
sprite: _Sunrise/FleshCult/FleshBodyMods/heavy_flesh_helmet.rsi
- type: Clothing
sprite: _Sunrise/FleshCult/FleshBodyMods/heavy_flesh_helmet.rsi
- type: PressureProtection
highPressureMultiplier: 0.3
lowPressureMultiplier: 1000
- type: Armor
modifiers:
coefficients:
Blunt: 0.95
Slash: 0.95
Piercing: 0.95

View file

@ -0,0 +1,11 @@
- type: entity
id: VentFleshWorms
parent: BaseGameRule
categories: [ HideSpawnMenu ]
components:
- type: StationEvent
earliestStart: 15
minimumPlayers: 20
weight: 5
duration: 60
- type: VentFleshWormsRule

View file

@ -0,0 +1,14 @@
- type: explosion
id: Flesh
damagePerIntensity:
types:
Heat: 5
Poison: 3
tileBreakChance: [0, 0.25, 0.5]
tileBreakIntensity: [0, 5, 15]
tileBreakRerollReduction: 20
lightColor: Red
fireColor: Red
texturePath: /Textures/Effects/fire.rsi
fireStates: 3
sound: /Audio/Effects/Fluids/splat.ogg

View file

@ -0,0 +1,66 @@
- type: entity
id: FleshHeart
parent: BaseStructure
name: Flesh Heart
description: Flesh Heart
placement:
mode: AlignTileAny
components:
- type: ContainerContainer
containers:
bodyContainer: !type:Container
- type: Appearance
- type: SpriteFade
- type: Sprite
netsync: false
drawdepth: Mobs
sprite: _Sunrise/FleshCult/flesh_heart.rsi
layers:
- state: base_heart
map: ["enum.FleshHeartLayers.Base"]
- type: Transform
anchored: true
- type: Physics
bodyType: Static
- type: Climbable
delay: 5
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeAabb
bounds: "-1.5,-1.4,1.5,0.3"
density: 50
mask:
- MachineMask
layer:
- MachineLayer
- type: Damageable
damageContainer: Biological
damageModifierSet: FleshHeart
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 1000
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: PointLight
radius: 10
energy: 1
castShadows: true
color: "#87031f"
- type: FleshHeart
finalState: "final_heart"
fleshTileId: Flesh
damageMobsIfHeartDestruct:
types:
Slash: 700
spawns:
MobFleshSpider: 0.20
MobFleshPudge: 0.20
MobFleshBall: 0.30
MobFleshBat: 0.30
spawnMobsAmount: 3
spawnMobsFrequency: 180

View file

@ -0,0 +1,65 @@
- type: entity
id: Flesh
name: flesh
description: A rapidly growing, dangerous plant. WHY ARE YOU STOPPING TO LOOK AT IT?!
placement:
mode: SnapgridCenter
snap:
- Wall
components:
- type: Tag
tags:
- Flesh
- type: MeleeSound
soundGroups:
Brute:
path:
"/Audio/Weapons/slash.ogg"
- type: Sprite
sprite: _Sunrise/FleshCult/flesh_tile.rsi
state: flesh_11
drawdepth: LowFloors
netsync: false
- type: Appearance
- type: Clickable
- type: Transform
anchored: true
- type: Physics
- type: Fixtures
fixtures:
fix1:
hard: false
density: 7
shape:
!type:PhysShapeAabb
bounds: "-0.5,-0.5,0.5,0.5"
layer:
- MidImpassable
- type: Damageable
damageModifierSet: Wood
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 10
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: Temperature
heatDamage:
types:
Heat: 5
coldDamage: {}
- type: Flammable
fireSpread: true
damage:
types:
Heat: 5
- type: Reactive
groups:
Flammable: [Touch]
Extinguish: [Touch]
- type: FireVisuals
sprite: Effects/fire.rsi
normalState: 1
- type: AtmosExposed

View file

@ -0,0 +1,45 @@
- type: entity
parent: BaseWall
id: WallFlesh
name: flesh wall
components:
- type: Appearance
- type: Sprite
sprite: _Sunrise/FleshCult/flesh_wall.rsi
- type: Icon
sprite: _Sunrise/FleshCult/flesh_wall.rsi
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 10
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- type: Damageable
damageModifierSet: Wood
- type: IconSmooth
key: walls
base: flesh
- type: Tag
tags:
- Flesh
- Wall
- Window
- type: Temperature
heatDamage:
types:
Heat: 5
coldDamage: {}
- type: Flammable
fireSpread: true
damage:
types:
Heat: 1
- type: Reactive
groups:
Flammable: [Touch]
Extinguish: [Touch]
- type: FireVisuals
sprite: Effects/fire.rsi
normalState: 1

View file

@ -0,0 +1,14 @@
- type: gamePreset
id: FleshCult
alias:
- fleshcult
- fs
name: flesh-cult-title
description: flesh-cult-description
showInVote: true
hide: true
rules:
- FleshCult
- LiteSubGamemodesRule
- BasicStationEventScheduler
- BasicRoundstartVariation

View file

@ -0,0 +1,137 @@
- type: entity
categories: [ HideSpawnMenu ]
name: Flesh Claw
parent: BaseItem
id: FleshClaw
description: Flesh Claw
suffix: Flesh Cult
components:
- type: FleshHandMod
- type: Unremoveable
deleteOnDrop: true
- type: Sprite
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_claw.rsi
state: icon
netsync: false
- type: Item
size: Huge
- type: ToolTileCompatible
- type: Tool
qualities:
- Prying
useSound: /Audio/Effects/gib2.ogg
- type: Prying
speedModifier: 2.5
pryPowered: true
force: true
- type: MeleeWeapon
damage:
types:
Slash: 13
- type: MultipleTool
statusShowBehavior: true
entries:
- behavior: Prying
sprite:
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_claw.rsi
state: icon
useSound: /Audio/Effects/gib2.ogg
changeSound: /Audio/Effects/gib3.ogg
- behavior: Cutting
sprite:
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_claw.rsi
state: icon
useSound: /Audio/Effects/gib2.ogg
changeSound: /Audio/Effects/gib3.ogg
- behavior: Anchoring
sprite:
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_claw.rsi
state: icon
useSound: /Audio/Effects/gib2.ogg
changeSound: /Audio/Effects/gib3.ogg
- behavior: Screwing
sprite:
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_claw.rsi
state: icon
useSound: /Audio/Effects/gib2.ogg
changeSound: /Audio/Effects/gib3.ogg
- type: entity
categories: [ HideSpawnMenu ]
name: flesh fist
parent: BaseItem
id: FleshFist
description: Fisting is three hundred bucks
suffix: Flesh Cult
components:
- type: FleshHandMod
- type: Unremoveable
deleteOnDrop: true
- type: Sprite
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_fist.rsi
state: icon
- type: MeleeWeapon
attackRate: 0.75
damage:
types:
Blunt: 20
Structural: 300
- type: Item
size: Ginormous
- type: entity
categories: [ HideSpawnMenu ]
name: Flesh blade
parent: BaseItem
id: FleshBlade
description: Flesh blade
suffix: Flesh Cult
components:
- type: FleshHandMod
- type: Unremoveable
deleteOnDrop: true
- type: Sharp
- type: Sprite
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_blade.rsi
state: icon
- type: MeleeWeapon
damage:
types:
Slash: 25
- type: Item
size: Huge
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_blade.rsi
- type: DisarmMalus
- type: entity
categories: [ HideSpawnMenu ]
name: spike gun
id: FleshSpikeHandGun
parent: BaseItem
suffix: Flesh Cult
components:
- type: FleshHandMod
- type: Sprite
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_spike_hand.rsi
state: icon
- type: Item
sprite: _Sunrise/FleshCult/FleshHandMods/flesh_spike_hand.rsi
size: Ginormous
- type: Gun
fireRate: 3
selectedMode: SemiAuto
availableModes:
- SemiAuto
soundGunshot:
path: /Audio/_Sunrise/FleshCult/spike_gun_shot.ogg
- type: AmmoCounter
- type: Appearance
- type: RechargeBasicEntityAmmo
rechargeCooldown: 2
rechargeSound: /Audio/_Sunrise/FleshCult/spike_gun_reload.ogg
- type: Unremoveable
deleteOnDrop: true
- type: BasicEntityAmmoProvider
proto: BulletSpike
capacity: 10
count: 5

View file

@ -0,0 +1,21 @@
- type: entity
parent: BaseMindRoleAntag
id: MindRoleFleshCultist
name: Flesh Cultist Role
# description: mind-role-flesh-cultist-description
components:
- type: MindRole
antagPrototype: FleshCultist
exclusiveAntag: true
- type: FleshCultistRole
- type: entity
parent: BaseMindRoleAntag
id: MindRoleFleshCultistLeader
name: Flesh Leader Cultist Role
# description: mind-role-flesh-leader-cultist-description
components:
- type: MindRole
antagPrototype: FleshCultistLeader
exclusiveAntag: true
- type: FleshCultistRole

View file

@ -0,0 +1,591 @@
- type: entity
parent: SimpleSpaceMobBase
id: BaseMobFleshCult
name: flesh mob
description: A shambling mass of flesh, animated through anomalous energy.
abstract: true
components:
- type: Reactive
groups:
Flammable: [Touch]
Extinguish: [Touch]
- type: Damageable
damageContainer: Biological
damageModifierSet: FleshMob
- type: HTN
rootTask:
task: SimpleHostileCompound
- type: NpcFactionMember
factions:
- Flesh
- type: Tag
tags:
- DoorBumpOpener
- Flesh
- CannotSuicide
- FootstepSound
- type: Sprite
drawdepth: Mobs
sprite: _Sunrise/FleshCult/flesh_cult_mobs.rsi
- type: MovementAlwaysTouching
- type: MovementSpeedModifier
baseWalkSpeed: 2
baseSprintSpeed: 3
- type: MobState
allowedStates:
- Alive
- Dead
- type: MobThresholds
thresholds:
0: Alive
75: Dead
- type: Appearance
- type: Butcherable
spawned:
- id: FoodMeat
amount: 1
- type: CombatMode
- type: MeleeWeapon
hidden: true
soundHit:
path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg
angle: 0
animation: WeaponArcClaw
damage:
types:
Slash: 3
- type: ReplacementAccent
accent: genericAggressive
- type: CollectiveMind
minds:
- FleshCult
- type: IgnoreFleshSpiderWeb
- type: entity
parent: BaseMobFleshCult
id: MobFleshSpider
components:
# - type: VentCrawler
- type: HTN
rootTask:
task: XenoCompound
blackboard:
NavInteract: !type:Bool
true
NavPry: !type:Bool
false
NavSmash: !type:Bool
true
- type: Butcherable
spawned:
- id: FoodMeat
amount: 2
- type: FleshMob
soundDeath: /Audio/_Sunrise/FleshCult/flesh_worm_dead.ogg
deathMobSpawnId: MobFleshWorm
deathMobSpawnCount: 2
- type: Sprite
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: spider
- type: Vocal
sounds:
Unsexed: FleshWormEmote
- type: DamageStateVisuals
states:
Alive:
Base: spider
Dead:
Base: spider_dead
- type: MobThresholds
thresholds:
0: Alive
125: Dead
- type: Bloodstream
bloodMaxVolume: 300
bloodReagent: Blood
- type: MovementSpeedModifier
baseWalkSpeed: 3
baseSprintSpeed: 4.5
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: mob-flesh-spider-ghost-role-name
description: mob-flesh-spider-ghost-role-name
rules: Не атакуйте культистов плоти, не ломайте ДАМы, сервера, консоли и прочее. Не делайте разгерметизацию.
- type: GhostTakeoverAvailable
- type: MeleeWeapon
hidden: true
soundHit:
path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg
angle: 0
animation: WeaponArcClaw
damage:
types:
Piercing: 5
Poison: 3
- type: Spider
webPrototype: FleshSpiderWeb
webAction: FleshSpiderWebAction
- type: Puller
needsHands: false
- type: FootstepModifier
footstepSoundCollection:
collection: FootstepSpiderLegs
params:
volume: 10
- type: entity
parent: BaseMobFleshCult
id: MobFleshPudge
components:
- type: Tool
qualities:
- Prying
- type: HTN
rootTask:
task: XenoCompound
blackboard:
NavInteract: !type:Bool
true
NavPry: !type:Bool
true
NavSmash: !type:Bool
true
- type: Prying
pryPowered: true
force: true
speedModifier: 1.5
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: Vocal
sounds:
Unsexed: FleshPudgeEmote
- type: FleshMob
deathMobSpawnCount: 3
deathMobSpawnId: MobFleshWorm
soundDeath:
path: /Audio/_Sunrise/FleshCult/flesh_pudge_dead.ogg
- type: Sprite
drawdepth: Mobs
sprite: _Sunrise/FleshCult/flesh_cult_pudge.rsi
layers:
- map: ["enum.DamageStateVisualLayers.Base"]
state: alive
- type: DamageStateVisuals
states:
Alive:
Base: alive
Dead:
Base: dead
- type: Bloodstream
bloodMaxVolume: 500
bloodReagent: Blood
- type: InputMover
- type: MobMover
- type: Physics
bodyType: Dynamic
- type: MovementSpeedModifier
baseWalkSpeed: 3.5
baseSprintSpeed: 4.5
- type: MobThresholds
thresholds:
0: Alive
250: Dead
- type: MobState
allowedStates:
- Alive
- Dead
- type: MeleeWeapon
altDisarm: false
hidden: true
soundHit:
path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg
animation: WeaponArcSmash
damage:
types:
Slash: 10
Blunt: 20
Structural: 300
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: mob-flesh-pudge-ghost-role-name
description: mob-flesh-pudge-ghost-role-name
rules: Не атакуйте культистов плоти, не ломайте ДАМы, сервера, консоли и прочее. Не делайте разгерметизацию.
- type: GhostTakeoverAvailable
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.40
density: 800
mask:
- MobMask
layer:
- MobLayer
- type: Puller
needsHands: false
- type: entity
parent: BaseMobFleshCult
id: MobFleshBall
components:
- type: FleshMob
soundDeath: "/Audio/_Sunrise/FleshCult/flesh_pudge_dead.ogg"
deathMobSpawnId: "MobFleshWorm"
deathMobSpawnCount: 0
- type: Sprite
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: ball
- type: MeleeWeapon
hidden: true
soundHit:
path: /Audio/Effects/bite.ogg
angle: 0
animation: WeaponArcSmash
damage:
types:
Piercing: 3
- type: DamageStateVisuals
states:
Alive:
Base: ball
Dead:
Base: ball_dead
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.25
density: 10
mask:
- FlyingMobMask
layer:
- FlyingMobLayer
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: mob-flesh-ball-ghost-role-name
description: mob-flesh-ball-ghost-role-name
rules: Не атакуйте культистов плоти, не ломайте ДАМы, сервера, консоли и прочее. Не делайте разгерметизацию.
- type: GhostTakeoverAvailable
- type: MobThresholds
thresholds:
0: Alive
25: Dead
- type: MovementSpeedModifier
baseWalkSpeed: 5
baseSprintSpeed: 6
- type: TriggerOnMobstateChange
mobState:
- Dead
- type: TriggerImplantAction
- type: ExplodeOnTrigger
- type: Explosive
explosionType: Flesh
totalIntensity: 50
intensitySlope: 3
maxIntensity: 100
canCreateVacuum: false
- type: entity
parent: BaseMobFleshCult
id: MobFleshBat
components:
- type: HTN
rootTask:
task: XenoCompound
blackboard:
NavInteract: !type:Bool
true
NavPry: !type:Bool
false
NavSmash: !type:Bool
true
- type: FleshMob
soundDeath: "/Audio/_Sunrise/FleshCult/flesh_worm_dead.ogg"
deathMobSpawnId: "MobFleshWorm"
deathMobSpawnCount: 0
- type: Sprite
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: bat
- type: DamageStateVisuals
states:
Alive:
Base: bat
Dead:
Base: bat_dead
- type: Bloodstream
bloodMaxVolume: 50
bloodReagent: Blood
- type: Vocal
sounds:
Unsexed: FleshWormEmote
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.25
density: 10
mask:
- FlyingMobMask
layer:
- FlyingMobLayer
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: mob-flesh-bat-ghost-role-name
description: mob-flesh-bat-ghost-role-name
rules: Не атакуйте культистов плоти, не ломайте ДАМы, сервера, консоли и прочее. Не делайте разгерметизацию.
- type: GhostTakeoverAvailable
- type: MeleeWeapon
hidden: true
soundHit:
path: /Audio/Effects/bite.ogg
angle: 0
animation: WeaponArcBite
damage:
types:
Piercing: 5
- type: MobThresholds
thresholds:
0: Alive
75: Dead
- type: MovementSpeedModifier
baseWalkSpeed: 5
baseSprintSpeed: 6
- type: Puller
needsHands: false
- type: entity
parent: BaseMobFleshCult
id: MobFleshHugger
components:
# - type: VentCrawler
- type: FleshMob
soundDeath: "/Audio/_Sunrise/FleshCult/flesh_worm_dead.ogg"
deathMobSpawnId: "MobFleshWorm"
deathMobSpawnCount: 1
- type: FleshHugger
paralyzeTime: 5
chansePounce: 33
damage:
types:
Piercing: 2
actionJump: FleshHuggerJump
actionGetOff: FleshHuggerGetOff
- type: Sprite
drawdepth: SmallMobs
noRot: true
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: fleshhugger
- type: DamageStateVisuals
states:
Alive:
Base: fleshhugger
Dead:
Base: fleshhugger_dead
- type: Vocal
sounds:
Unsexed: FleshWormEmote
- type: Clothing
quickEquip: false
sprite: _Sunrise/FleshCult/flesh_cult_mobs.rsi
equippedPrefix: fleshhugger
slots:
- MASK
- type: Physics
bodyType: Dynamic
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.2
density: 100
mask:
- SmallMobMask
layer:
- SmallMobLayer
- type: Speech
- type: Bloodstream
bloodMaxVolume: 10
bloodReagent: Blood
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: mob-flesh-hugger-ghost-role-name
description: mob-flesh-hugger-ghost-role-name
rules: Не атакуйте культистов плоти, не ломайте ДАМы, сервера, стены и окна если за ними нету людей, консоли и прочее.
- type: GhostTakeoverAvailable
- type: MeleeWeapon
hidden: true
soundHit:
path: /Audio/Effects/bite.ogg
angle: 0
animation: WeaponArcBite
damage:
types:
Piercing: 2
- type: MobThresholds
thresholds:
0: Alive
25: Dead
- type: MovementSpeedModifier
baseWalkSpeed: 3.5
baseSprintSpeed: 4.5
- type: Tag
tags:
- Flesh
- CannotSuicide
- type: entity
parent: BaseMobFleshCult
id: MobFleshWorm
components:
- type: FleshMob
soundDeath: /Audio/_Sunrise/FleshCult/flesh_worm_dead.ogg
deathMobSpawnId: MobSmallFleshWorm
deathMobSpawnCount: 2
- type: Sprite
drawdepth: SmallMobs
noRot: true
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: worm
- type: DamageStateVisuals
states:
Alive:
Base: worm
Dead:
Base: worm_dead
- type: Vocal
sounds:
Unsexed: FleshWormEmote
- type: Physics
bodyType: Dynamic
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeCircle
radius: 0.2
density: 25
mask:
- SmallMobMask
layer:
- SmallMobLayer
- type: Speech
- type: Bloodstream
bloodMaxVolume: 10
bloodReagent: Blood
- type: MeleeWeapon
hidden: true
soundHit:
path: /Audio/Effects/bite.ogg
angle: 0
animation: WeaponArcBite
damage:
types:
Piercing: 1
- type: MobThresholds
thresholds:
0: Alive
15: Dead
- type: MovementSpeedModifier
baseWalkSpeed: 4
baseSprintSpeed: 5
- type: Tag
tags:
- Flesh
- CannotSuicide
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 25
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: entity
parent: MobFleshWorm
id: MobSmallFleshWorm
components:
- type: FleshMob
soundDeath: /Audio/_Sunrise/FleshCult/flesh_worm_dead.ogg
deathMobSpawnId: MobSmallFleshWorm
deathMobSpawnCount: 0
- type: Sprite
drawdepth: SmallMobs
noRot: true
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: small_worm
- type: DamageStateVisuals
states:
Alive:
Base: small_worm
Dead:
Base: small_worm_dead
- type: MobThresholds
thresholds:
0: Alive
5: Dead
- type: MeleeWeapon
hidden: true
soundHit:
path: /Audio/Effects/bite.ogg
angle: 0
animation: WeaponArcBite
damage:
types:
Piercing: 0.5
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 15
behaviors:
- !type:SpawnEntitiesBehavior
spawn:
FoodMeat:
min: 1
max: 1
- !type:DoActsBehavior
acts: [ "Destruction" ]

View file

@ -0,0 +1,9 @@
- type: damageModifierSet
id: FleshMob
coefficients:
Heat: 1.50
- type: damageModifierSet
id: FleshHeart
coefficients:
Heat: 2.00

View file

@ -0,0 +1,38 @@
- type: entity
abstract: true
parent: BaseObjective
id: BaseFleshCultObjective
components:
- type: Objective
difficulty: 1.5
issuer: flesh-cult
- type: RoleRequirement
roles:
components:
- FleshCultistRole
- type: entity
categories: [ HideSpawnMenu ]
parent: BaseFleshCultObjective
id: CreateFleshHeartObjective
name: Создать и пробудить сердце плоти.
description: Вам необходимо развить необходимый навык для создания сердца.
Для пробуждения оно должно поглотить нужное количество тел развитых существ.
После пробуждения будьте готовы его защищать, ведь оно начнет превращать всю станцию в плоть.
components:
- type: Objective
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistFleshHeart.png
- type: NumberObjective
min: 1
max: 1
- type: CreateFleshHeartCondition
- type: entity
categories: [ HideSpawnMenu ]
parent: [BaseFleshCultObjective, BaseSurviveObjective]
id: FleshCultSurviveObjective
name: Выжить и сохранить человеческий облик.
description: Не допустите голодания паразита, чтобы избежать непредвиденных обстоятельств.
components:
- type: Objective
icon: _Sunrise/FleshCult/Interface/Actions/fleshCultistSurvivalObjective.png

View file

@ -0,0 +1,40 @@
- type: entity
id: BulletSplashAcid
name: flesh acid spit
parent: BaseBulletTrigger
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _Sunrise/FleshCult/flesh_toxic.rsi
layers:
- state: flesh_toxic
- type: Projectile
damage:
types:
Caustic: 1
- type: SplashOnTrigger
splashReagents:
reagents:
- ReagentId: FleshAcid
Quantity: 30
- type: entity
id: BulletSpike
name: spike
parent: BaseBullet
categories: [ HideSpawnMenu ]
description:
components:
- type: Sprite
netsync: false
noRot: false
sprite: _Sunrise/FleshCult/flesh_projectiles.rsi
layers:
- state: spike
shader: unshaded
- type: Projectile
impactEffect: BulletImpactEffectKinetic
damage:
types:
Piercing: 13

View file

@ -0,0 +1,48 @@
- type: entity
id: FleshCult
parent: BaseGameRule
categories: [ HideSpawnMenu ]
components:
- type: GameRule
minPlayers: 20
- type: FleshCultRule
faction: Flesh
- type: AntagObjectives
objectives:
- CreateFleshHeartObjective
- FleshCultSurviveObjective
- type: AntagSelection
agentName: flesh-cult-round-end-agent-name
definitions:
- prefRoles: [ FleshCultistLeader ]
fallbackRoles: [ FleshCultist ]
max: 1
startingGear: FleshCultistLeaderGear
components:
- type: FleshCultist
- type: NpcFactionMember
factions:
- FleshHuman
blacklist:
components:
- AntagImmune
- Synth
- BibleUser
mindRoles:
- MindRoleFleshCultistLeader
- prefRoles: [ FleshCultist ]
fallbackRoles: [ FleshCultistLeader ]
max: 3
playerRatio: 15
components:
- type: FleshCultist
- type: NpcFactionMember
factions:
- FleshHuman
blacklist:
components:
- AntagImmune
- Synth
- BibleUser
mindRoles:
- MindRoleFleshCultist

View file

@ -0,0 +1,13 @@
- type: emoteSounds
id: FleshPudgeEmote
sound:
path: /Audio/_Sunrise/FleshCult/flesh_pudge_dead.ogg
params:
variation: 0.125
- type: emoteSounds
id: FleshWormEmote
sound:
path: /Audio/_Sunrise/FleshCult/flesh_worm_scream.ogg
params:
variation: 0.125

View file

@ -0,0 +1,28 @@
- type: entity
parent: SpiderWeb
id: FleshSpiderWeb
name: spider web
description: It's stringy and sticky.
placement:
mode: SnapgridCenter
snap:
- Wall
components:
- type: Sprite
sprite: _Sunrise/FleshCult/fleshspiderweb.rsi
layers:
- state: spider_web_1
map: [ "spiderWebLayer" ]
drawdepth: WallMountedItems
- type: GenericVisualizer
visuals:
enum.SpiderWebVisuals.Variant:
spiderWebLayer:
1: {state: spider_web_1}
2: {state: spider_web_2}
- type: SpeedModifierContacts
walkSpeedModifier: 0.3
sprintSpeedModifier: 0.3
ignoreWhitelist:
components:
- IgnoreFleshSpiderWeb

View file

@ -0,0 +1,7 @@
- type: startingGear
id: FleshCultistLeaderGear
storage:
back:
- SyringeCarolNT
- SyringeCarolNT
- SyringeCarolNT

View file

@ -0,0 +1,11 @@
- type: factionIcon
id: FleshFaction
priority: 12
showTo:
components:
- ShowAntagIcons
- FleshCultist
- FleshMob
icon:
sprite: /Textures/_Sunrise/FleshCult/Interface/flesh_icon.rsi
state: Flesh

View file

@ -0,0 +1,5 @@
- type: Tag
id: FullBodyOuter
- type: Tag
id: Directional

View file

@ -0,0 +1,94 @@
- type: reagent
id: FleshAcid
name: reagent-name-flesh-acid
group: Toxins
desc: reagent-desc-flesh-acid
physicalDesc: reagent-physical-desc-strong-smelling
flavor: acid
color: "#c9000e"
boilingPoint: 0.0
meltingPoint: 0.0
reactiveEffects:
Acidic:
methods: [ Touch ]
effects:
- !type:HealthChange
conditions:
- !type:HasTag
tag: "Flesh"
invert: true
scaleByQuantity: false
ignoreResistances: true
damage:
types:
Caustic: 10
- !type:Emote
conditions:
- !type:HasTag
tag: "Flesh"
invert: true
emote: Scream
probability: 0.9
- !type:PopupMessage
conditions:
- !type:HasTag
tag: "Flesh"
invert: true
type: Local
visualType: Large
messages: [ "generic-reagent-effect-burning-insides" ]
probability: 0.9
- type: reagent
id: Carol
name: reagent-name-carol
group: Toxins
desc: reagent-desc-carol
physicalDesc: reagent-physical-desc-necrotic
flavor: bitter
color: "#c9000e"
metabolisms:
Medicine:
effects:
- !type:CauseFleshCultInfection
conditions:
- !type:HasTag
tag: "Flesh"
invert: true
- !type:ReagentThreshold
min: 5
- !type:ModifyBloodLevel
conditions:
- !type:HasTag
tag: "Flesh"
amount: 5
- !type:SatiateThirst
conditions:
- !type:HasTag
tag: "Flesh"
factor: 2.5
- !type:SatiateHunger
conditions:
- !type:HasTag
tag: "Flesh"
factor: 2.5
- !type:HealthChange
conditions:
- !type:HasTag
tag: "Flesh"
damage:
groups:
Airloss: -4
types:
Heat: -1
Shock: -2
Cold: -2
Poison: -2
Piercing: -2
Blunt: -2
Caustic: -2
Slash: -2

View file

@ -116,3 +116,4 @@
- Saboteurs
- Furs
- Crew
- VirusFlesh

View file

@ -115,3 +115,4 @@
- Saboteurs
- Furs
- Crew
- VirusFlesh

Some files were not shown because too many files have changed in this diff Show more