Cult update (#220)

* - tweak: Cult door not bump openable.

* - tweak: Better summoning and Narsie

* - tweak: Construct update.

* - tweak: Eldrich blade fits in suit storage.

* - tweak: More spell limit.

* - fix: Fix pylon desc.

* - tweak: Teleport works on cuffed targets.

* - tweak: More popups if target is holy.

* - fix: No rune drawing using fingers.

* - tweak: Better pylon placement & less pylon healing range.

* - tweak: More blood rites charge.

* - fix: Fix max spell amount.

* - tweak: Less cult door and wall health.

* - fix: Constructs are dead IC.

* - add: Revive rune now notifies player.

* - add: Narsie summon rune eui.

* - fix: Fix narsie summon sound not playing for reapers.

* - tweak: Whatever.

* - add: Conceal presence spell.

* - tweak: Tweakz.

* - add: Blood spear.

* - add: Blood boil barrage.

* - tweak: Artificer flies.

* - tweak: Blood bolt color tweaks.

* - tweak: Runic door is bump openable again.

* - fix: Fix concealed door outline.

* - add: Update concealable name and desc.

* - tweak: Remove the unremoveable.

* - tweak: Gift ignore.

* - add: Organs regenerate on rejuvenate.

* - tweak: Brainless cultist is fine.

* - add: Added more fun.

* - add: Add rune descriptions.

* - fix: Fixes.

* - tweak: Blood rites now uses verb.

* - tweak: Bring it back.
This commit is contained in:
Aviu00
2024-03-22 17:23:33 +09:00
committed by GitHub
parent 1337e4d26e
commit 74ef19d6a6
109 changed files with 1831 additions and 182 deletions

View File

@@ -62,13 +62,14 @@ public sealed partial class CultRuleComponent : Component
public List<ConstructComponent> Constructs = new();
public CultWinCondition WinCondition;
public CultWinCondition WinCondition = CultWinCondition.CultDraw;
}
public enum CultWinCondition : byte
{
CultDraw,
CultWin,
CultFailure
CultFailure,
}
public sealed class CultNarsieSummoned : EntityEventArgs

View File

@@ -1,14 +1,15 @@
using System.Linq;
using Content.Server._Miracle.Components;
using Content.Server._Miracle.GulagSystem;
using Content.Server.Actions;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Server.NPC.Systems;
using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components;
using Content.Server.StationEvents.Components;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Body.Systems;
using Content.Shared.Humanoid;
@@ -25,6 +26,7 @@ using Robust.Shared.Random;
using Content.Shared._White;
using Content.Shared._White.Chaplain;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Systems;
using Content.Shared.Mind;
using Robust.Shared.Audio.Systems;
@@ -47,6 +49,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly GulagSystem _gulag = default!;
[Dependency] private readonly BloodSpearSystem _bloodSpear = default!;
private ISawmill _sawmill = default!;
@@ -69,6 +72,13 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
SubscribeLocalEvent<CultistComponent, ComponentInit>(OnCultistComponentInit);
SubscribeLocalEvent<CultistComponent, ComponentRemove>(OnCultistComponentRemoved);
SubscribeLocalEvent<CultistComponent, MobStateChangedEvent>(OnCultistsStateChanged);
SubscribeLocalEvent<CultistRoleComponent, GetBriefingEvent>(OnGetBriefing);
}
private void OnGetBriefing(Entity<CultistRoleComponent> ent, ref GetBriefingEvent args)
{
args.Append(Loc.GetString("cult-role-briefing-short"));
}
private void OnCultistsStateChanged(EntityUid uid, CultistComponent component, MobStateChangedEvent ev)
@@ -130,7 +140,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
if (!TryComp<MobStateComponent>(owner, out var mobState))
continue;
if (_mobStateSystem.IsAlive(owner, mobState))
if (!_mobStateSystem.IsDead(owner, mobState))
{
aliveCultists++;
}
@@ -140,7 +150,10 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
return;
cultistsRule.WinCondition = CultWinCondition.CultFailure;
_roundEndSystem.EndRound();
// Check for all in once gamemode
if (!GameTicker.GetActiveGameRules().Where(HasComp<RampingStationEventSchedulerComponent>).Any())
_roundEndSystem.EndRound();
}
private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args)
@@ -165,6 +178,8 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
}
UpdateCultistsAppearance(cultistsRule);
component.OriginalMind = (mindComponent.Mind.Value, Comp<MindComponent>(mindComponent.Mind.Value));
}
private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args)
@@ -177,6 +192,8 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
cultistsRule.CurrentCultists.Remove(component);
_bloodSpear.DetachSpearFromUser((uid, component));
foreach (var empower in component.SelectedEmpowers)
{
_actions.RemoveAction(uid, GetEntity(empower));
@@ -438,7 +455,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
PrototypeId = cultistRule.CultistRolePrototype
};
_roleSystem.MindAddRole(mindId, cultistComponent);
_roleSystem.MindAddRole(mindId, cultistComponent, mind);
EnsureComp<CultistComponent>(playerEntity);
_factionSystem.RemoveFaction(playerEntity, "NanoTrasen", false);
@@ -478,19 +495,27 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
_roundEndSystem.EndRound();
var query = EntityQueryEnumerator<MobStateComponent, MindContainerComponent, CultistComponent>();
var query =
EntityQueryEnumerator<MobStateComponent, MindContainerComponent, CultistComponent, TransformComponent>();
while (query.MoveNext(out var uid, out _, out var mindContainer, out _))
List<Entity<MindContainerComponent, TransformComponent>> cultists = new();
while (query.MoveNext(out var uid, out _, out var mindContainer, out _, out var transform))
{
if (!mindContainer.HasMind || mindContainer.Mind is null)
cultists.Add((uid, mindContainer, transform));
}
foreach (var ent in cultists)
{
if (ent.Comp1.Mind is null)
{
continue;
}
var reaper = Spawn(CultRuleComponent.ReaperPrototype, Transform(uid).Coordinates);
_mindSystem.TransferTo(mindContainer.Mind.Value, reaper);
var reaper = Spawn(CultRuleComponent.ReaperPrototype, ent.Comp2.Coordinates);
_mindSystem.TransferTo(ent.Comp1.Mind.Value, reaper);
_bodySystem.GibBody(uid);
_bodySystem.GibBody(ent);
}
}

View File

@@ -13,6 +13,7 @@ using Content.Shared.Popups;
using Content.Shared.Tag;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Pylon;
using Content.Shared._White.Cult.Systems;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Audio;
@@ -40,6 +41,8 @@ public sealed class PylonSystem : EntitySystem
[Dependency] private readonly BloodstreamSystem _blood = default!;
[Dependency] private readonly TurfSystem _turf = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly PointLightSystem _pointLight = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
public override void Initialize()
{
@@ -47,6 +50,15 @@ public sealed class PylonSystem : EntitySystem
SubscribeLocalEvent<SharedPylonComponent, InteractHandEvent>(OnInteract);
SubscribeLocalEvent<SharedPylonComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SharedPylonComponent, ConcealEvent>(OnConceal);
}
private void OnConceal(Entity<SharedPylonComponent> ent, ref ConcealEvent args)
{
ent.Comp.Activated = !args.Conceal;
UpdateAppearance(ent, ent.Comp);
_pointLight.SetEnabled(ent, !args.Conceal);
_physics.SetCanCollide(ent, !args.Conceal);
}
private void OnInit(EntityUid uid, SharedPylonComponent component, ComponentInit args)
@@ -172,7 +184,7 @@ public sealed class PylonSystem : EntitySystem
if (player.AttachedEntity is not { Valid: true } playerEntity)
continue;
if (!EntityManager.TryGetComponent<CultistComponent>(playerEntity, out _))
if (!HasComp<CultistComponent>(playerEntity) && !HasComp<ConstructComponent>(playerEntity))
continue;
if (_mobStateSystem.IsDead(playerEntity))
@@ -221,12 +233,7 @@ public sealed class PylonSystem : EntitySystem
UpdateAppearance(uid, comp);
if (!TryComp<PointLightComponent>(uid, out var light))
return;
#pragma warning disable RA0002
light.Enabled = comp.Activated;
#pragma warning restore RA0002
_pointLight.SetEnabled(uid, comp.Activated);
var toggleMsg = Loc.GetString(comp.Activated ? "pylon-toggle-on" : "pylon-toggle-off");
_popupSystem.PopupEntity(toggleMsg, uid);

View File

@@ -1,6 +1,7 @@
using Content.Server.UserInterface;
using Content.Shared._White.Cult.UI;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Shared._White.Cult;
@@ -11,5 +12,5 @@ public sealed partial class RuneDrawerProviderComponent : Component
public Enum UserInterfaceKey = ListViewSelectorUiKey.Key;
[DataField("runePrototypes")]
public List<string> RunePrototypes = new();
public List<EntProtoId> RunePrototypes = new();
}

View File

@@ -1,10 +1,12 @@
using System.Linq;
using System.Numerics;
using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Emp;
using Content.Server.EUI;
using Content.Server._White.Cult.UI;
using Content.Shared._White.Chaplain;
using Content.Shared._White.Cult;
using Content.Shared.Chemistry.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
@@ -17,12 +19,19 @@ using Content.Shared.Stacks;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
using Content.Shared._White.Cult.Actions;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Systems;
using Content.Shared._White.Cult.UI;
using Content.Shared.Actions;
using Content.Shared.Cuffs.Components;
using Content.Shared.DoAfter;
using Content.Shared.Maps;
using Content.Shared.Mindshield.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Player;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
@@ -36,6 +45,13 @@ public partial class CultSystem
[Dependency] private readonly EuiManager _euiManager = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly TileSystem _tile = default!;
[Dependency] private readonly BloodSpearSystem _bloodSpear = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
private const string TileId = "CultFloor";
private const string ConcealedTileId = "CultFloorConcealed";
public void InitializeActions()
{
@@ -44,8 +60,10 @@ public partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultShadowShacklesTargetActionEvent>(OnShadowShackles);
SubscribeLocalEvent<CultistComponent, CultElectromagneticPulseInstantActionEvent>(OnElectromagneticPulse);
SubscribeLocalEvent<CultistComponent, CultSummonCombatEquipmentTargetActionEvent>(OnSummonCombatEquipment);
SubscribeLocalEvent<CultistComponent, CultConcealPresenceWorldActionEvent>(OnConcealPresence);
SubscribeLocalEvent<CultistComponent, CultConcealInstantActionEvent>(OnConcealPresence);
SubscribeLocalEvent<CultistComponent, CultRevealInstantActionEvent>(OnConcealPresence);
SubscribeLocalEvent<CultistComponent, CultBloodRitesInstantActionEvent>(OnBloodRites);
SubscribeLocalEvent<CultistComponent, CultBloodSpearRecallInstantActionEvent>(OnBloodSpearRecall);
SubscribeLocalEvent<CultistComponent, CultTeleportTargetActionEvent>(OnTeleport);
SubscribeLocalEvent<CultistComponent, CultStunTargetActionEvent>(OnStunTarget);
SubscribeLocalEvent<CultistComponent, ActionGettingRemovedEvent>(OnActionRemoved);
@@ -74,9 +92,15 @@ public partial class CultSystem
private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args)
{
if (args.Target == uid || !TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) ||
HasComp<HolyComponent>(args.Target) || !TryComp<StatusEffectsComponent>(args.Target, out var status))
!TryComp<StatusEffectsComponent>(args.Target, out var status))
return;
if (HasComp<HolyComponent>(args.Target))
{
_popupSystem.PopupEntity("Священная сила препятствует магии.", args.Performer, args.Performer);
return;
}
if (HasComp<MindShieldComponent>(args.Target))
{
_popupSystem.PopupEntity("Он имплантирован чипом защиты разума.", args.Performer, args.Performer);
@@ -94,14 +118,23 @@ public partial class CultSystem
private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args)
{
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) || !TryComp<ActorComponent>(uid, out var actor))
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) ||
!TryComp<ActorComponent>(uid, out var actor))
return;
if (!TryComp<CultistComponent>(args.Target, out _) &&
!(TryComp<MobStateComponent>(args.Target, out var mobStateComponent) &&
mobStateComponent.CurrentState is not MobState.Alive))
if (HasComp<HolyComponent>(args.Target))
{
_popupSystem.PopupEntity("Цель должна быть культистом или лежать.", args.Performer, args.Performer);
_popupSystem.PopupEntity("Священная сила препятствует магии.", args.Performer, args.Performer);
return;
}
if (!HasComp<CultistComponent>(args.Target) && !HasComp<ConstructComponent>(args.Target) &&
(!TryComp<MobStateComponent>(args.Target, out var mobStateComponent) ||
mobStateComponent.CurrentState is MobState.Alive) &&
(!TryComp<CuffableComponent>(args.Target, out var cuffable) || cuffable.Container.Count == 0))
{
_popupSystem.PopupEntity("Цель должна быть культистом, быть связанной или лежать.", args.Performer,
args.Performer);
return;
}
@@ -147,25 +180,28 @@ public partial class CultSystem
totalBloodAmount += solutionContent.Quantity;
_bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f);
_bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f, bloodstreamComponent);
_solutionSystem.RemoveReagent((Entity<SolutionComponent>) solution, "Blood", FixedPoint2.MaxValue);
if (GetMissingBloodValue(bloodstreamComponent) == 0)
/*if (GetMissingBloodValue(bloodstreamComponent) == 0)
{
breakLoop = true;
}
}*/
}
}
if (totalBloodAmount == 0f)
{
return;
}
component.RitesBloodAmount += totalBloodAmount;
_audio.PlayPvs("/Audio/White/Cult/enter_blood.ogg", uid, AudioParams.Default);
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(bruteDamageGroup, -20));
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(burnDamageGroup, -20));
_popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-message", ("blood", component.RitesBloodAmount)), uid,
uid);
args.Handled = true;
}
@@ -174,10 +210,163 @@ public partial class CultSystem
return bloodstreamComponent.BloodMaxVolume - bloodstreamComponent.BloodSolution!.Value.Comp.Solution.Volume;
}
private void OnConcealPresence(EntityUid uid, CultistComponent component, CultConcealPresenceWorldActionEvent args)
private void OnBloodSpearRecall(Entity<CultistComponent> ent, ref CultBloodSpearRecallInstantActionEvent args)
{
if (!TryComp<BloodstreamComponent>(args.Performer, out _))
if (ent.Comp.BloodSpear == null)
{
_bloodSpear.DetachSpearFromUser(ent);
return;
}
var spear = ent.Comp.BloodSpear.Value;
var xform = Transform(spear);
var coords = _transform.GetWorldPosition(xform);
var userCoords = _transform.GetWorldPosition(ent);
var distance = (userCoords - coords).Length();
if (distance > 10f)
{
_popupSystem.PopupEntity("Копьё слишком далеко!", ent, ent);
return;
}
TryComp<PhysicsComponent>(spear, out var physics);
_physics.SetBodyType(spear, BodyType.Dynamic, body: physics, xform: xform);
_transform.AttachToGridOrMap(spear, xform);
_transform.SetWorldPosition(xform, userCoords);
_handsSystem.TryPickupAnyHand(ent, spear, animate: false);
args.Handled = true;
}
private void OnConcealPresence(EntityUid uid, CultistComponent component, CultConcealPresenceInstantActionEvent args)
{
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) ||
!TryComp<TransformComponent>(args.Performer, out var xform))
return;
var conceal = args is CultConcealInstantActionEvent;
var concealableQuery = GetEntityQuery<ConcealableComponent>();
var appearanceQuery = GetEntityQuery<AppearanceComponent>();
const float radius = 5f;
var entitiesInRange = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(xform), radius);
var success = false;
// Conceal/Reveal runes and structures
foreach (var ent in entitiesInRange)
{
if (!concealableQuery.TryGetComponent(ent, out var concealable) ||
!appearanceQuery.TryGetComponent(ent, out var appearance) ||
!EntityManager.MetaQuery.TryGetComponent(ent, out var meta))
continue;
if (concealable.Concealed == conceal)
continue;
_appearanceSystem.SetData(ent, ConcealableAppearance.Concealed, conceal, appearance);
concealable.Concealed = conceal;
RaiseLocalEvent(ent, new ConcealEvent(conceal));
if (concealable.ChangeMeta)
{
if (conceal)
{
_metaDataSystem.SetEntityName(ent, concealable.ConcealedName, meta);
_metaDataSystem.SetEntityDescription(ent, concealable.ConcealedDesc, meta);
}
else
{
_metaDataSystem.SetEntityName(ent, concealable.RevealedName, meta);
_metaDataSystem.SetEntityDescription(ent, concealable.RevealedDesc, meta);
}
}
Dirty(ent, concealable, meta);
success = true;
}
var gridUid = xform.GridUid;
var pos = xform.Coordinates;
var cultTileDef = (ContentTileDefinition) _tileDefinition[TileId];
var concealedTileDef = (ContentTileDefinition) _tileDefinition[ConcealedTileId];
// Conceal/Reveal tiles
if (TryComp(gridUid, out MapGridComponent? mapGrid))
{
var tileRefs = _mapSystem.GetLocalTilesIntersecting(gridUid.Value, mapGrid,
new Box2(pos.Position + new Vector2(-radius, -radius), pos.Position + new Vector2(radius, radius)));
foreach (var tile in tileRefs)
{
var tilePos = _turf.GetTileCenter(tile);
if (!pos.InRange(EntityManager, _transform, tilePos, radius))
continue;
if (conceal)
{
if (tile.Tile.TypeId != cultTileDef.TileId)
continue;
_tile.ReplaceTile(tile, concealedTileDef);
success = true;
}
else
{
if (tile.Tile.TypeId != concealedTileDef.TileId)
continue;
_tile.ReplaceTile(tile, cultTileDef);
success = true;
}
}
}
if (success)
{
_audio.PlayPvs(conceal ? "/Audio/White/Cult/smoke.ogg" : "/Audio/White/Cult/enter_blood.ogg", uid,
AudioParams.Default.WithMaxDistance(2f));
_bloodstreamSystem.TryModifyBloodLevel(uid, -2, bloodstream, createPuddle: false);
args.Handled = true;
}
var spellQuery = GetEntityQuery<ConcealPresenceSpellComponent>();
var actionQuery = GetEntityQuery<InstantActionComponent>();
// Alter spell concealing/revealing state
foreach (var empower in component.SelectedEmpowers)
{
if (empower == null)
continue;
var ent = GetEntity(empower.Value);
if (!spellQuery.TryGetComponent(ent, out var spell) ||
!actionQuery.TryGetComponent(ent, out var action))
continue;
if (conceal)
{
spell.Revealing = true;
action.Icon = spell.RevealIcon;
action.Event = spell.RevealEvent;
}
else
{
spell.Revealing = false;
action.Icon = spell.ConcealIcon;
action.Event = spell.ConcealEvent;
}
Dirty(ent, action);
}
}
private void OnSummonCombatEquipment(
@@ -300,7 +489,7 @@ public partial class CultSystem
var xform = Transform(args.Performer).Coordinates;
var dagger = _entityManager.SpawnEntity(RitualDaggerPrototypeId, xform);
_bloodstreamSystem.TryModifyBloodLevel(args.Performer, -20, bloodstreamComponent, false);
_bloodstreamSystem.TryModifyBloodLevel(args.Performer, -10, bloodstreamComponent, false);
_handsSystem.TryPickupAnyHand(args.Performer, dagger);
args.Handled = true;
}

View File

@@ -10,10 +10,12 @@ using Content.Server.Hands.Systems;
using Content.Server.Weapons.Ranged.Systems;
using Content.Server._White.Cult.GameRule;
using Content.Server._White.Cult.Runes.Comps;
using Content.Server._White.Cult.UI;
using Content.Server.Bible.Components;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.Components;
using Content.Server.Ghost;
using Content.Shared._White.Chaplain;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Cuffs.Components;
@@ -36,10 +38,13 @@ using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Runes;
using Content.Shared._White.Cult.UI;
using Content.Shared.Cuffs;
using Content.Shared.GameTicking;
using Content.Shared.Mindshield.Components;
using Content.Shared.Pulling;
using Content.Shared.UserInterface;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Physics.Events;
@@ -92,7 +97,8 @@ public sealed partial class CultSystem : EntitySystem
SubscribeLocalEvent<CultEmpowerComponent, ActivateInWorldEvent>(OnActiveInWorld);
// UI
SubscribeLocalEvent<RuneDrawerProviderComponent, UseInHandEvent>(OnRuneDrawerUseInHand);
SubscribeLocalEvent<RuneDrawerProviderComponent, ActivatableUIOpenAttemptEvent>(OnRuneDrawAttempt);
SubscribeLocalEvent<RuneDrawerProviderComponent, BeforeActivatableUIOpenEvent>(BeforeRuneDraw);
SubscribeLocalEvent<RuneDrawerProviderComponent, ListViewItemSelectedMessage>(OnRuneSelected);
SubscribeLocalEvent<CultTeleportRuneProviderComponent, TeleportRunesListWindowItemSelectedMessage>(
OnTeleportRuneSelected);
@@ -107,6 +113,8 @@ public sealed partial class CultSystem : EntitySystem
SubscribeLocalEvent<CultRuneBaseComponent, CultEraseEvent>(OnErase);
SubscribeLocalEvent<CultRuneBaseComponent, StartCollideEvent>(HandleCollision);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
InitializeBuffSystem();
InitializeNarsie();
InitializeSoulShard();
@@ -134,30 +142,30 @@ public sealed partial class CultSystem : EntitySystem
private readonly SoundPathSpecifier _magic = new("/Audio/White/Cult/magic.ogg");
private readonly SoundPathSpecifier _apocRuneStartDrawing = new("/Audio/White/Cult/startdraw.ogg");
private readonly SoundPathSpecifier _apocRuneEndDrawing = new("/Audio/White/Cult/finisheddraw.ogg");
private readonly SoundPathSpecifier _narsie40Sec = new("/Audio/White/Cult/40sec.ogg");
private Entity<AudioComponent>? _narsieSummonningAudio = null;
private void OnRoundRestart(RoundRestartCleanupEvent ev)
{
CultRuneReviveComponent.ChargesLeft = 3;
}
/*
* Rune draw start ----
*/
private void OnRuneDrawerUseInHand(EntityUid uid, RuneDrawerProviderComponent component, UseInHandEvent args)
private void OnRuneDrawAttempt(Entity<RuneDrawerProviderComponent> ent, ref ActivatableUIOpenAttemptEvent args)
{
if (!_ui.TryGetUi(uid, component.UserInterfaceKey, out _))
return;
if (!TryComp<ActorComponent>(args.User, out var actorComponent))
return;
if (!HasComp<CultistComponent>(args.User))
return;
args.Cancel();
}
if (_ui.TryGetUi(uid, ListViewSelectorUiKey.Key, out var bui))
{
_ui.SetUiState(bui, new ListViewBUIState(component.RunePrototypes, false));
_ui.OpenUi(bui, actorComponent.PlayerSession);
}
private void BeforeRuneDraw(Entity<RuneDrawerProviderComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
if (_ui.TryGetUi(ent, ListViewSelectorUiKey.Key, out var bui))
_ui.SetUiState(bui, new ListViewBUIState(ent.Comp.RunePrototypes, true));
}
private void OnRuneSelected(EntityUid uid, RuneDrawerProviderComponent component, ListViewItemSelectedMessage args)
@@ -185,16 +193,20 @@ public sealed partial class CultSystem : EntitySystem
if (HasComp<CultBuffComponent>(whoCalled))
_timeToDraw /= 2;
if (runePrototype == ApocalypseRunePrototypeId)
{
_timeToDraw = 120.0f;
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-started-drawing-rune-end"), "CULT", true,
_apocRuneStartDrawing, colorOverride: Color.DarkRed);
}
if (!IsAllowedToDraw(whoCalled))
return false;
if (runePrototype == ApocalypseRunePrototypeId)
{
if (!_mindSystem.TryGetMind(whoCalled, out _, out var mind) ||
mind.Session is not { } playerSession)
return false;
_euiManager.OpenEui(new ApocalypseRuneEui(whoCalled, _entityManager), playerSession);
return true;
}
var ev = new CultDrawEvent
{
Rune = runePrototype
@@ -769,7 +781,7 @@ public sealed partial class CultSystem : EntitySystem
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-started"), "CULT", false,
colorOverride: Color.DarkRed);
_audio.PlayGlobal(_narsie40Sec, Filter.Broadcast(), false, AudioParams.Default.WithLoop(true).WithVolume(0.15f));
_narsieSummonningAudio = _audio.PlayGlobal(_narsie40Sec, Filter.Broadcast(), false, AudioParams.Default.WithLoop(true).WithVolume(0.15f));
return true;
}
@@ -778,6 +790,8 @@ public sealed partial class CultSystem : EntitySystem
{
_doAfterAlreadyStarted = false;
_audio.Stop(_narsieSummonningAudio?.Owner, _narsieSummonningAudio?.Comp);
if (args.Cancelled)
{
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-prevented"), "CULT", false,
@@ -790,13 +804,13 @@ public sealed partial class CultSystem : EntitySystem
if (transform == null)
return;
_entityManager.SpawnEntity(NarsiePrototypeId, transform.Value);
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summoned"), "CULT", true, _apocRuneEndDrawing,
colorOverride: Color.DarkRed);
var ev = new CultNarsieSummoned();
RaiseLocalEvent(ev);
_entityManager.SpawnEntity(NarsiePrototypeId, transform.Value);
//_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summoned"), "CULT", true, _apocRuneEndDrawing,
// colorOverride: Color.DarkRed);
}
/*
@@ -852,6 +866,27 @@ public sealed partial class CultSystem : EntitySystem
CultRuneReviveComponent.ChargesLeft--;
_entityManager.EventBus.RaiseLocalEvent(target, new RejuvenateEvent());
EntityUid? transferTo = null;
if (!_mindSystem.TryGetMind(target, out var mindId, out var mind))
{
if (!TryComp<CultistComponent>(target, out var cultist) || cultist.OriginalMind == null)
return true;
(mindId, mind) = cultist.OriginalMind.Value;
transferTo = target;
}
if (mind.Session is not { } playerSession)
return true;
// notify them they're being revived.
if (mind.CurrentEntity != target)
{
_euiManager.OpenEui(new ReturnToBodyEui(mind, _mindSystem, mindId, transferTo), playerSession);
}
return true;
}
@@ -1258,6 +1293,8 @@ public sealed partial class CultSystem : EntitySystem
return;
}
var damage = 10;
if (rune == ApocalypseRunePrototypeId)
{
if (!_entityManager.TryGetComponent(uid, out TransformComponent? transComp))
@@ -1265,6 +1302,7 @@ public sealed partial class CultSystem : EntitySystem
return;
}
damage = 40;
var pos = transComp.MapPosition;
var x = (int) pos.X;
var y = (int) pos.Y;
@@ -1273,7 +1311,7 @@ public sealed partial class CultSystem : EntitySystem
"CULT", true, _apocRuneEndDrawing, colorOverride: Color.DarkRed);
}
var damageSpecifier = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), 10);
var damageSpecifier = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), damage);
_damageableSystem.TryChangeDamage(uid, damageSpecifier, true, false);
_xform.AttachToGridOrMap(_entityManager.SpawnEntity(rune, transform.Value));

View File

@@ -2,6 +2,7 @@ using System.Linq;
using Content.Server.Body.Components;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.UI;
using Content.Shared.DoAfter;
using Content.Shared.Verbs;
using Robust.Shared.Player;
@@ -16,6 +17,7 @@ public sealed partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected);
SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove);
SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated);
SubscribeLocalEvent<CultistComponent, CultistFactoryItemSelectedMessage>(OnBloodRitesSelected);
}
private void OnCultistEmpowerRemove(Entity<CultistComponent> ent, ref CultEmpowerRemoveBuiMessage args)
@@ -28,7 +30,7 @@ public sealed partial class CultSystem
private void OnSpellCreated(EntityUid ent, CultistComponent comp, SpellCreatedEvent args)
{
if (args.Cancelled || comp.SelectedEmpowers.Count >= 1)
if (args.Cancelled || comp.SelectedEmpowers.Count >= 2)
return;
var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.Spell));
@@ -55,7 +57,7 @@ public sealed partial class CultSystem
if (action == null)
return;
if (comp.SelectedEmpowers.Count >= 1)
if (comp.SelectedEmpowers.Count >= 2)
{
_popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent, ent);
return;
@@ -98,8 +100,24 @@ public sealed partial class CultSystem
}
};
var bloodRitesVerb = new Verb
{
Text = Loc.GetString("verb-blood-rites-text"),
Message = Loc.GetString("verb-blood-rites-message", ("blood", ent.Comp.RitesBloodAmount)),
Category = VerbCategory.Cult,
Act = () =>
{
if (!_ui.TryGetUi(ent, BloodRitesUi.Key, out var bui))
return;
_ui.SetUiState(bui, new CultistFactoryBUIState(ent.Comp.BloodRites));
_ui.OpenUi(bui, actor.PlayerSession);
}
};
args.Verbs.Add(createSpellVerb);
args.Verbs.Add(removeSpellVerb);
args.Verbs.Add(bloodRitesVerb);
}
private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session)
@@ -112,4 +130,36 @@ public sealed partial class CultSystem
_ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session);
}
private void OnBloodRitesSelected(Entity<CultistComponent> ent, ref CultistFactoryItemSelectedMessage args)
{
if (!_prototypeManager.TryIndex<CultistFactoryProductionPrototype>(args.Item, out var prototype))
return;
if (ent.Comp.RitesBloodAmount < prototype.BloodCost)
{
var message = Loc.GetString("verb-blood-rites-no-blood", ("required", prototype.BloodCost),
("blood", ent.Comp.RitesBloodAmount));
_popupSystem.PopupEntity(message, ent, ent);
return;
}
var success = false;
foreach (var item in prototype.Item)
{
var entity = Spawn(item, Transform(ent).Coordinates);
if (_handsSystem.TryPickupAnyHand(ent, entity))
{
success = true;
continue;
}
_popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-no-hand"), ent, ent);
QueueDel(entity);
break;
}
if (success)
ent.Comp.RitesBloodAmount -= prototype.BloodCost;
}
}

View File

@@ -1,13 +1,19 @@
using Content.Server.Doors.Systems;
using Content.Server.Cuffs;
using Content.Server.Doors.Systems;
using Content.Shared._White.Chaplain;
using Content.Shared.Doors;
using Content.Shared.Humanoid;
using Content.Shared.Stunnable;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Systems;
using Content.Shared.Cuffs.Components;
using Content.Shared.Doors.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Weapons.Melee.Components;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
@@ -19,6 +25,9 @@ public sealed class RunicDoorSystem : EntitySystem
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly OccluderSystem _occluder = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly CuffableSystem _cuffable = default!;
public override void Initialize()
{
@@ -27,6 +36,31 @@ public sealed class RunicDoorSystem : EntitySystem
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
SubscribeLocalEvent<RunicDoorComponent, AttackedEvent>(OnGetAttacked);
SubscribeLocalEvent<RunicDoorComponent, ConcealEvent>(OnConceal);
}
private void OnConceal(Entity<RunicDoorComponent> ent, ref ConcealEvent args)
{
if (!TryComp(ent, out MetaDataComponent? meta))
return;
if (TryComp(ent, out PhysicsComponent? physics))
_occluder.SetEnabled(ent, args.Conceal && physics.CanCollide, meta: meta);
if (TryComp(ent, out DoorComponent? door))
{
door.Occludes = args.Conceal;
Dirty(ent, door, meta);
}
if (!TryComp(ent, out MeleeSoundComponent? meleeSound) || meleeSound.SoundGroups == null)
return;
meleeSound.SoundGroups["Brute"] = args.Conceal
? new SoundPathSpecifier("/Audio/Weapons/smash.ogg")
: new SoundCollectionSpecifier("GlassSmash");
Dirty(ent, meleeSound, meta);
}
private void OnGetAttacked(Entity<RunicDoorComponent> ent, ref AttackedEvent args)
@@ -79,7 +113,8 @@ public sealed class RunicDoorSystem : EntitySystem
_doorSystem.Deny(airlock);
if (!HasComp<HumanoidAppearanceComponent>(user) || HasComp<HolyComponent>(user))
if (!HasComp<HumanoidAppearanceComponent>(user) || HasComp<HolyComponent>(user) ||
TryComp(airlock, out ConcealableComponent? concealable) && concealable.Concealed)
return false;
var direction = Transform(user).MapPosition.Position - Transform(airlock).MapPosition.Position;
@@ -90,4 +125,11 @@ public sealed class RunicDoorSystem : EntitySystem
_stunSystem.TryParalyze(user, TimeSpan.FromSeconds(3), true);
return false;
}
public bool CanBumpOpen(EntityUid uid, EntityUid otherUid)
{
return !HasComp<RunicDoorComponent>(uid) || !HasComp<ConstructComponent>(otherUid) &&
(!HasComp<CultistComponent>(otherUid) || !_mobState.IsAlive(otherUid) ||
TryComp(otherUid, out CuffableComponent? cuffable) && _cuffable.GetAllCuffs(cuffable).Count > 0);
}
}

View File

@@ -5,6 +5,7 @@ using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Structures;
using Content.Shared._White.Cult.Systems;
using Content.Shared._White.Cult.UI;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
@@ -28,6 +29,7 @@ public sealed class CultistFactorySystem : EntitySystem
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
private const string RitualDaggerPrototypeId = "RitualDagger";
@@ -42,6 +44,12 @@ public sealed class CultistFactorySystem : EntitySystem
SubscribeLocalEvent<CultistFactoryComponent, InteractUsingEvent>(TryToggleAnchor);
SubscribeLocalEvent<CultistFactoryComponent, CultAnchorDoAfterEvent>(OnAnchorDoAfter);
SubscribeLocalEvent<CultistFactoryComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<CultistFactoryComponent, ConcealEvent>(OnConceal);
}
private void OnConceal(Entity<CultistFactoryComponent> ent, ref ConcealEvent args)
{
_physics.SetCanCollide(ent, !args.Conceal);
}
public override void Update(float frameTime)

View File

@@ -0,0 +1,61 @@
using Content.Server.Chat.Systems;
using Content.Server.DoAfter;
using Content.Server.EUI;
using Content.Shared._White.Cult.Runes;
using Content.Shared._White.Cult.UI;
using Content.Shared.DoAfter;
using Content.Shared.Eui;
using Robust.Server.Audio;
using Robust.Shared.Audio;
namespace Content.Server._White.Cult.UI;
public sealed class ApocalypseRuneEui : BaseEui
{
private readonly SoundPathSpecifier _apocRuneStartDrawing = new("/Audio/White/Cult/startdraw.ogg");
private const string ApocalypseRunePrototypeId = "ApocalypseRune";
private readonly EntityUid _whoCalled;
private readonly IEntityManager _entityManager;
public ApocalypseRuneEui(EntityUid whoCalled, IEntityManager entityManager)
{
_whoCalled = whoCalled;
_entityManager = entityManager;
}
public override void HandleMessage(EuiMessageBase msg)
{
base.HandleMessage(msg);
if (msg is not ApocalypseRuneDrawMessage {Accepted: true})
{
Close();
return;
}
var ev = new CultDrawEvent
{
Rune = ApocalypseRunePrototypeId
};
var argsDoAfterEvent = new DoAfterArgs(_entityManager, _whoCalled, 120f, ev, _whoCalled)
{
BreakOnUserMove = true,
NeedHand = true
};
if (!_entityManager.System<DoAfterSystem>().TryStartDoAfter(argsDoAfterEvent))
{
Close();
return;
}
_entityManager.System<ChatSystem>().DispatchGlobalAnnouncement(Loc.GetString("cult-started-drawing-rune-end"),
"CULT", true, _apocRuneStartDrawing, colorOverride: Color.DarkRed);
_entityManager.System<AudioSystem>().PlayPvs("/Audio/White/Cult/butcher.ogg", _whoCalled,
AudioParams.Default.WithMaxDistance(2f));
Close();
}
}