Skip to content

Commit

Permalink
Blood Cult DLC 1: Make It an Actually Playable Game (Mode) (#1276)
Browse files Browse the repository at this point in the history
<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

You don't actually have to pay for it, y'know?
---

# TODO

<!--
A list of everything you have to do before this PR is "complete"
You probably won't have to complete everything before merging but it's
good to leave future references
-->

- [x] Fix bugs from discord thread. 

---

<!--
This is default collapsed, readers click to expand it and see all your
media
The PR media section can get very large at times, so this is a good way
to keep it clean
The title is written using HTML tags
The title must be within the <summary> tags or you won't see it
-->

<details><summary><h1>Media</h1></summary>
<p>

![Example Media Embed](https://example.com/thisimageisntreal.png)

</p>
</details>

---

# Changelog

<!--
You can add an author after the `:cl:` to change the name that appears
in the changelog (ex: `:cl: Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

:cl:
- add: In-game guide book to kickstart your sinister activities.
- add: Constructs now have abilities.
- add: Rending rune and apocalypse rune now should only be placed in the
specific spots on maps. Needs to be mapped.
- add: Veil Shifter now displays how much charges it has when examining.
- add: Cult runes now have descriptions. Also stating how much invokers
required for each rune.
- add: Blood rites can now be dropped&deleted.
- add: Blood rites now suck... blood in 0.5 tiles radius.
- remove: Non-cultists can no longer examine runes.
- fix: Fixed Cult Objective Target selection. You can (and should)
sacrifice your own people now.
- fix: Non cultists can no longer use veil shifter.
- fix: Teleport spell is no more a cheap rip-off and now actually
teleports.
- fix: Timed Factories can't no more produce infinite number of
entities.
- fix: Offering rune should now properly convert someone.
- fix: Sacrificing body with mind now properly transfers their mind to
soul shard.
- fix: Shadow Shackles now cuffs the target instead of the caster
(lmao).

---------

Signed-off-by: Remuchi <[email protected]>
Signed-off-by: Remuchi <[email protected]>
Co-authored-by: Raphael Bertoche <[email protected]>
Co-authored-by: VMSolidus <[email protected]>
  • Loading branch information
3 people authored Dec 11, 2024
1 parent 48d63ba commit 975c673
Show file tree
Hide file tree
Showing 58 changed files with 1,326 additions and 292 deletions.
2 changes: 1 addition & 1 deletion Content.Client/ListViewSelector/ListViewSelectorBUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private void PopulateWindow(List<ListViewSelectorEntry> items)
{
var itemName = item.Name;
var itemDesc = item.Description;
if (_prototypeManager.TryIndex(item.Id, out var itemPrototype))
if (_prototypeManager.TryIndex(item.Id, out var itemPrototype, false))
{
itemName = itemPrototype.Name;
itemDesc = itemPrototype.Description;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Content.Shared.WhiteDream.BloodCult.Constructs.PhaseShift;

namespace Content.Client.WhiteDream.BloodCult.PhaseShift;

public sealed class PhaseShiftSystem : SharedPhaseShiftSystem;
61 changes: 29 additions & 32 deletions Content.Client/WhiteDream/BloodCult/Runes/UI/RuneDrawerBUI.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Linq;
using System.Numerics;
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.WhiteDream.BloodCult.Runes;
using JetBrains.Annotations;
Expand All @@ -23,71 +22,72 @@ public sealed class RuneDrawerBUI : BoundUserInterface
[Dependency] private readonly IInputManager _inputManager = default!;

private readonly SpriteSystem _spriteSystem;

private RadialMenu? _menu;
private readonly RadialMenu _menu;

public RuneDrawerBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_spriteSystem = _entManager.System<SpriteSystem>();
_menu = new()
{
HorizontalExpand = true,
VerticalExpand = true,
BackButtonStyleClass = "RadialMenuBackButton",
CloseButtonStyleClass = "RadialMenuCloseButton"
};
}

protected override void Open()
{
_menu = FormMenu();
_menu.OnClose += Close;
_menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / _displayManager.ScreenSize);
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_menu?.Dispose();
_menu.Close();
}

private RadialMenu FormMenu()
protected override void UpdateState(BoundUserInterfaceState state)
{
var menu = new RadialMenu
{
HorizontalExpand = true,
VerticalExpand = true,
BackButtonStyleClass = "RadialMenuBackButton",
CloseButtonStyleClass = "RadialMenuCloseButton"
};

if (!_entManager.HasComponent<RuneDrawerComponent>(Owner))
return menu;
if (state is RuneDrawerMenuState runeDrawerState)
FillMenu(runeDrawerState.AvailalbeRunes);
}

var runeSelectorArray = _protoManager.EnumeratePrototypes<RuneSelectorPrototype>().OrderBy(r => r.ID).ToArray();
private void FillMenu(List<ProtoId<RuneSelectorPrototype>>? runes = null)
{
if (runes is null)
return;

var mainContainer = new RadialContainer
var container = new RadialContainer
{
Radius = 36f / (runeSelectorArray.Length == 1
? 1
: MathF.Sin(MathF.PI / runeSelectorArray.Length))
Radius = 48f + 24f * MathF.Log(runes.Count)
};

foreach (var runeSelector in runeSelectorArray)
_menu.AddChild(container);

foreach (var runeSelector in runes)
{
if (!_protoManager.TryIndex(runeSelector.Prototype, out var proto))
if (!_protoManager.TryIndex(runeSelector, out var runeSelectorProto) ||
!_protoManager.TryIndex(runeSelectorProto.Prototype, out var runeProto))
continue;

var itemSize = new Vector2(64f, 64f);
var button = new RadialMenuTextureButton
{
ToolTip = Loc.GetString(proto.Name),
ToolTip = Loc.GetString(runeProto.Name),
StyleClasses = { "RadialMenuButton" },
SetSize = itemSize
};

var runeIcon = _spriteSystem.Frame0(proto);
var runeIcon = _spriteSystem.Frame0(runeProto);
var runeScale = itemSize / runeIcon.Size;

var texture = new TextureRect
{
VerticalAlignment = Control.VAlignment.Center,
HorizontalAlignment = Control.HAlignment.Center,
Texture = _spriteSystem.Frame0(proto),
Texture = _spriteSystem.Frame0(runeProto),
TextureScale = runeScale
};

Expand All @@ -99,10 +99,7 @@ private RadialMenu FormMenu()
Close();
};

mainContainer.AddChild(button);
container.AddChild(button);
}

menu.AddChild(mainContainer);
return menu;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public sealed partial class BloodRitesAuraComponent : Component
[DataField]
public FixedPoint2 TotalHealing = 20;

[DataField]
public float PuddleConsumeRadius = 0.5f;

[DataField]
public SoundSpecifier BloodRitesAudio = new SoundPathSpecifier(
new ResPath("/Audio/WhiteDream/BloodCult/rites.ogg"),
Expand Down
58 changes: 47 additions & 11 deletions Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using Content.Shared.Damage.Prototypes;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Fluids.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.UserInterface;
Expand All @@ -34,6 +36,7 @@ public sealed class BloodRitesSystem : EntitySystem
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly HandsSystem _handsSystem = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
Expand All @@ -52,6 +55,8 @@ public override void Initialize()

SubscribeLocalEvent<BloodRitesAuraComponent, BeforeActivatableUIOpenEvent>(BeforeUiOpen);
SubscribeLocalEvent<BloodRitesAuraComponent, BloodRitesMessage>(OnRitesMessage);

SubscribeLocalEvent<BloodRitesAuraComponent, DroppedEvent>(OnDropped);
}

private void OnExamining(Entity<BloodRitesAuraComponent> rites, ref ExaminedEvent args) =>
Expand Down Expand Up @@ -83,19 +88,16 @@ private void OnAfterInteract(Entity<BloodRitesAuraComponent> rites, ref AfterInt
return;
}

if (!TryComp(args.Target, out SolutionContainerManagerComponent? solutionContainer)) // please send help
return;

foreach (var (_, solution) in _solutionContainer.EnumerateSolutions((args.Target.Value, solutionContainer)))
if (HasComp<PuddleComponent>(args.Target))
{
// I don't think something will ever have more than 1000 blood units in it's solution...
rites.Comp.StoredBlood += solution.Comp.Solution.RemoveReagent(_bloodProto, 1000);
_solutionContainer.UpdateChemicals(solution);
break;
ConsumePuddles(args.Target.Value, rites);
args.Handled = true;
}
else if (TryComp(args.Target, out SolutionContainerManagerComponent? solutionContainer))
{
ConsumeBloodFromSolution((args.Target.Value, solutionContainer), rites);
args.Handled = true;
}

_audio.PlayPvs(rites.Comp.BloodRitesAudio, rites);
args.Handled = true;
}

private void OnDoAfter(Entity<BloodRitesAuraComponent> rites, ref BloodRitesExtractDoAfterEvent args)
Expand Down Expand Up @@ -154,6 +156,8 @@ private void OnRitesMessage(Entity<BloodRitesAuraComponent> rites, ref BloodRite
_handsSystem.TryPickup(args.Actor, ent);
}

private void OnDropped(Entity<BloodRitesAuraComponent> rites, ref DroppedEvent args) => QueueDel(rites);

private bool Heal(Entity<BloodRitesAuraComponent> rites, EntityUid user, Entity<DamageableComponent> target)
{
if (target.Comp.TotalDamage == 0)
Expand Down Expand Up @@ -234,4 +238,36 @@ Entity<BloodstreamComponent> target
rites.Comp.StoredBlood -= bloodCost;
return true;
}

private void ConsumePuddles(EntityUid origin, Entity<BloodRitesAuraComponent> rites)
{
var coords = Transform(origin).Coordinates;

var lookup = _lookup.GetEntitiesInRange<PuddleComponent>(
coords,
rites.Comp.PuddleConsumeRadius,
LookupFlags.Uncontained);

foreach (var puddle in lookup)
{
if (!TryComp(puddle, out SolutionContainerManagerComponent? solutionContainer))
continue;
ConsumeBloodFromSolution((puddle, solutionContainer), rites);
}

_audio.PlayPvs(rites.Comp.BloodRitesAudio, rites);
}

private void ConsumeBloodFromSolution(
Entity<SolutionContainerManagerComponent?> ent,
Entity<BloodRitesAuraComponent> rites
)
{
foreach (var (_, solution) in _solutionContainer.EnumerateSolutions(ent))
{
rites.Comp.StoredBlood += solution.Comp.Solution.RemoveReagent(_bloodProto, 1000);
_solutionContainer.UpdateChemicals(solution);
break;
}
}
}
67 changes: 67 additions & 0 deletions Content.Server/WhiteDream/BloodCult/ConstructActionsSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Content.Server.WhiteDream.BloodCult.Constructs.PhaseShift;
using Content.Shared.StatusEffect;
using Content.Shared.WhiteDream.BloodCult.Spells;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using PhaseShiftedComponent = Content.Shared.WhiteDream.BloodCult.Constructs.PhaseShift.PhaseShiftedComponent;

namespace Content.Server.WhiteDream.BloodCult;

public sealed class ConstructActionsSystem : EntitySystem
{
[Dependency] private readonly ITileDefinitionManager _tileDef = default!;

[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;

private const string CultTileSpawnEffect = "CultTileSpawnEffect";

public override void Initialize()
{
SubscribeLocalEvent<PlaceTileEntityEvent>(OnPlaceTileEntityEvent);
SubscribeLocalEvent<PhaseShiftEvent>(OnPhaseShift);
}

private void OnPlaceTileEntityEvent(PlaceTileEntityEvent args)
{
if (args.Handled)
return;

if (args.Entity is { } entProtoId)
Spawn(entProtoId, args.Target);

if (args.TileId is { } tileId)
{
if (_transform.GetGrid(args.Target) is not { } grid || !TryComp(grid, out MapGridComponent? mapGrid))
return;

var tileDef = _tileDef[tileId];
var tile = new Tile(tileDef.TileId);

_mapSystem.SetTile(grid, mapGrid, args.Target, tile);
Spawn(CultTileSpawnEffect, args.Target);
}

if (args.Audio is { } audio)
_audio.PlayPvs(audio, args.Target);

args.Handled = true;
}

private void OnPhaseShift(PhaseShiftEvent args)
{
if (args.Handled)
return;

if (_statusEffects.TryAddStatusEffect<PhaseShiftedComponent>(
args.Performer,
args.StatusEffectId,
args.Duration,
false))
args.Handled = true;
}
}
31 changes: 19 additions & 12 deletions Content.Server/WhiteDream/BloodCult/Constructs/ConstructSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Server.WhiteDream.BloodCult.Gamerule;
using Content.Server.Actions;
using Content.Server.WhiteDream.BloodCult.Gamerule;
using Content.Shared.WhiteDream.BloodCult;
using Content.Shared.WhiteDream.BloodCult.Constructs;
using Robust.Server.GameObjects;
Expand All @@ -7,13 +8,14 @@ namespace Content.Server.WhiteDream.BloodCult.Constructs;

public sealed class ConstructSystem : EntitySystem
{
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<ConstructComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<ConstructComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ConstructComponent, ComponentShutdown>(OnComponentShutdown);
}

Expand All @@ -37,23 +39,28 @@ public override void Update(float frameTime)
}
}

private void OnComponentStartup(Entity<ConstructComponent> ent, ref ComponentStartup args)
private void OnMapInit(Entity<ConstructComponent> construct, ref MapInitEvent args)
{
_appearanceSystem.SetData(ent, ConstructVisualsState.Transforming, true);
ent.Comp.Transforming = true;
var cultistRule = EntityManager.EntityQueryEnumerator<BloodCultRuleComponent>();
while (cultistRule.MoveNext(out _, out var rule))
foreach (var actionId in construct.Comp.Actions)
{
rule.Constructs.Add(ent);
var action = _actions.AddAction(construct, actionId);
construct.Comp.ActionEntities.Add(action);
}

_appearanceSystem.SetData(construct, ConstructVisualsState.Transforming, true);
construct.Comp.Transforming = true;
var cultistRule = EntityManager.EntityQueryEnumerator<BloodCultRuleComponent>();
while (cultistRule.MoveNext(out _, out var rule))
rule.Constructs.Add(construct);
}

private void OnComponentShutdown(Entity<ConstructComponent> ent, ref ComponentShutdown args)
private void OnComponentShutdown(Entity<ConstructComponent> construct, ref ComponentShutdown args)
{
foreach (var actionEntity in construct.Comp.ActionEntities)
_actions.RemoveAction(actionEntity);

var cultistRule = EntityManager.EntityQueryEnumerator<BloodCultRuleComponent>();
while (cultistRule.MoveNext(out _, out var rule))
{
rule.Constructs.Remove(ent);
}
rule.Constructs.Remove(construct);
}
}
Loading

0 comments on commit 975c673

Please sign in to comment.