Merge pull request #6197 from sowelipililimute/work/densetsu/glyphed

get glyphed idiot
This commit is contained in:
Vanessa 2026-07-06 15:13:01 -05:00 committed by GitHub
commit 0f98f0aada
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 182 additions and 138 deletions

View File

@ -0,0 +1,53 @@
using Content.Client.UserInterface.Controls;
using Content.Shared._DV.CosmicCult;
using Content.Shared._DV.CosmicCult.Prototypes;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client._DV.CosmicCult.UI;
[UsedImplicitly]
public sealed class CosmicGlyphDrawBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
private SimpleRadialMenu? _menu;
protected override void Open()
{
base.Open();
IoCManager.InjectDependencies(this);
_menu = this.CreateWindow<SimpleRadialMenu>();
_menu.OpenOverMouseScreenPosition();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not CosmicGlyphDrawBuiState glyphState || _menu == null)
return;
var options = new List<RadialMenuOptionBase>();
foreach (var glyphId in glyphState.Glyphs)
{
var glyph = _prototype.Index(glyphId);
options.Add(new RadialMenuActionOption<ProtoId<GlyphPrototype>>(SelectGlyph, glyph.ID)
{
IconSpecifier = RadialMenuIconSpecifier.With(glyph.Entity),
ToolTip = $"{Loc.GetString(glyph.Name)}\n{Loc.GetString(glyph.Tooltip)}",
});
}
_menu.SetButtons(options);
}
private void SelectGlyph(ProtoId<GlyphPrototype> glyphId)
{
SendMessage(new CosmicGlyphDrawSelectedMessage(glyphId));
}
}

View File

@ -19,9 +19,6 @@ public sealed class MonumentBoundUserInterface(EntityUid owner, Enum uiKey) : Bo
_menu = this.CreateWindow<MonumentMenu>();
_menu.OnSelectGlyphButtonPressed += protoId => SendMessage(new GlyphSelectedMessage(protoId));
_menu.OnRemoveGlyphButtonPressed += () => SendMessage(new GlyphRemovedMessage());
_menu.OnGainButtonPressed += OnInfluenceSelected;
}

View File

@ -44,15 +44,6 @@
<Label HorizontalAlignment="Center" Name="CrewToConvertUntilNextStage" FontColorOverride="#4CA7AD" StyleClasses="LabelSmall" />
</BoxContainer>
</BoxContainer>
<!-- Second section (Glyphs) -->
<BoxContainer Name="GlyphArea" HorizontalExpand="True" MinHeight="75" Orientation="Vertical" Margin="0 20 0 0">
<Label HorizontalAlignment="Center" Text="{Loc 'monument-interface-glyphs-title'}" />
<PanelContainer StyleClasses="LowDivider" />
<!-- Filled out programatically -->
<GridContainer HorizontalAlignment="Center" Name="GlyphContainer" Columns="3" Margin="0 5" />
<Button Name="SelectGlyphButton" Text="{Loc 'monument-interface-glyphs-button-scribe'}" SetHeight="50" Margin="0 10 0 0" StyleClasses="ButtonColorPurpleAndCool" />
<Button Name="RemoveGlyphButton" Text="{Loc 'monument-interface-glyphs-button-unscribe'}" SetHeight="50" Margin="0 10 0 0" StyleClasses="ButtonColorPurpleAndCool" />
</BoxContainer>
</BoxContainer>
<!-- Right section -->
<BoxContainer VerticalExpand="True" HorizontalExpand="True" Orientation="Vertical">

View File

@ -32,10 +32,6 @@ public sealed partial class MonumentMenu : FancyWindow
// All influence prototypes
private readonly IEnumerable<InfluencePrototype> _influencePrototypes;
private readonly ButtonGroup _glyphButtonGroup;
private ProtoId<GlyphPrototype> _selectedGlyphProtoId = string.Empty;
private HashSet<ProtoId<GlyphPrototype>> _unlockedGlyphProtoIds = [];
public Action<ProtoId<GlyphPrototype>>? OnSelectGlyphButtonPressed;
public Action? OnRemoveGlyphButtonPressed;
public Action<ProtoId<InfluencePrototype>>? OnGainButtonPressed;
private int _entropyPerCultist = 0;
@ -52,9 +48,6 @@ public sealed partial class MonumentMenu : FancyWindow
_glyphButtonGroup = new ButtonGroup();
RemoveGlyphButton.OnPressed += _ => OnRemoveGlyphButtonPressed?.Invoke();
SelectGlyphButton.OnPressed += _ => OnSelectGlyphButtonPressed?.Invoke(_selectedGlyphProtoId);
_cfg.OnValueChanged(DCCVars.CosmicCultistEntropyValue, entropy =>
{
_entropyPerCultist = entropy;
@ -64,15 +57,11 @@ public sealed partial class MonumentMenu : FancyWindow
public void UpdateState(MonumentBuiState state)
{
_selectedGlyphProtoId = state.SelectedGlyph;
_unlockedGlyphProtoIds = state.UnlockedGlyphs;
CultProgressBar.BackgroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = new Color(15, 17, 30) };
CultProgressBar.ForegroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = new Color(91, 62, 124) };
UpdateBar(state);
UpdateEntropy(state);
UpdateGlyphs();
UpdateInfluences(state);
}
@ -110,37 +99,6 @@ public sealed partial class MonumentMenu : FancyWindow
CrewToConvertUntilNextStage.Text = crewToNextStage.ToString();
}
// Update all the glyph buttons
private void UpdateGlyphs()
{
GlyphContainer.RemoveAllChildren();
foreach (var glyph in _glyphPrototypes)
{
var boxContainer = new BoxContainer();
var unlocked = _unlockedGlyphProtoIds.Contains(glyph.ID);
var button = new Button
{
HorizontalExpand = true,
StyleClasses = { StyleClass.ButtonSquare },
ToolTip = Loc.GetString(glyph.Tooltip),
Group = _glyphButtonGroup,
Pressed = glyph.ID == _selectedGlyphProtoId,
Disabled = !unlocked,
Modulate = !unlocked ? Color.Gray : Color.White,
};
button.OnPressed += _ => _selectedGlyphProtoId = glyph.ID;
var glyphIcon = new TextureRect
{
Texture = _sprite.Frame0(glyph.Icon),
TextureScale = new Vector2(2f, 2f),
Stretch = TextureRect.StretchMode.KeepCentered,
};
button.AddChild(glyphIcon);
boxContainer.AddChild(button);
GlyphContainer.AddChild(boxContainer);
}
}
// Update all the influence thingies
private void UpdateInfluences(MonumentBuiState state)
{

View File

@ -1,14 +1,20 @@
using System.Linq;
using Content.Server.Popups;
using Content.Shared._DV.CosmicCult.Components.Examine;
using Content.Shared._DV.CosmicCult.Components;
using Content.Shared._DV.CosmicCult.Prototypes;
using Content.Shared._DV.CosmicCult;
using Content.Shared.Actions.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Humanoid;
using Content.Shared.Interaction;
using Content.Shared.UserInterface;
using Content.Shared.Mobs.Systems;
using Content.Shared.Verbs;
using Robust.Server.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server._DV.CosmicCult;
@ -21,8 +27,11 @@ public sealed class CosmicGlyphSystem : SharedCosmicGlyphSystem
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly CosmicCultRuleSystem _cultRule = default!;
[Dependency] private readonly SharedCosmicCultSystem _cosmicCult = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
[Dependency] private readonly IGameTiming _timing = default!;
private readonly HashSet<Entity<CosmicCultComponent>> _cultists = [];
@ -32,9 +41,72 @@ public sealed class CosmicGlyphSystem : SharedCosmicGlyphSystem
{
SubscribeLocalEvent<CosmicGlyphComponent, ActivateInWorldEvent>(OnUseGlyph);
SubscribeLocalEvent<CosmicGlyphComponent, ComponentStartup>(OnGlyphCreated);
SubscribeLocalEvent<CosmicGlyphComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAlternativeVerbs);
SubscribeLocalEvent<CosmicCultComponent, EventCosmicDrawGlyph>(OnDrawGlyph);
SubscribeLocalEvent<CosmicGlyphDrawComponent, CosmicGlyphDrawSelectedMessage>(OnGlyphSelected);
base.Initialize();
}
private void OnGetAlternativeVerbs(Entity<CosmicGlyphComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess || !_cosmicCult.EntityIsCultist(args.User) || ent.Comp.State == GlyphStatus.Despawning)
return;
args.Verbs.Add(new AlternativeVerb
{
Text = Loc.GetString("cult-glyph-verb-erase"),
Act = () => EraseGlyph(ent),
});
}
private void OnDrawGlyph(Entity<CosmicCultComponent> ent, ref EventCosmicDrawGlyph args)
{
if (_cultRule.AssociatedGamerule(ent) is not { } cult ||
!TryComp<CosmicGlyphDrawComponent>(args.Action, out var drawComp))
return;
args.Handled = true;
var glyphs = GetAvailableGlyphs(cult.Comp.CurrentTier);
if (glyphs.Count == 0)
{
drawComp.Target = null;
_ui.CloseUi(args.Action.Owner, CosmicGlyphDrawUiKey.Key, args.Performer);
return;
}
drawComp.Target = args.Target;
_ui.OpenUi(args.Action.Owner, CosmicGlyphDrawUiKey.Key, args.Performer);
_ui.SetUiState(args.Action.Owner, CosmicGlyphDrawUiKey.Key, new CosmicGlyphDrawBuiState(glyphs));
}
private void OnGlyphSelected(Entity<CosmicGlyphDrawComponent> ent, ref CosmicGlyphDrawSelectedMessage args)
{
if (ent.Comp.Target is not { } target || !TryComp<ActionComponent>(ent, out var action) || action.Container != args.Actor
|| !TryComp<CosmicCultComponent>(args.Actor, out _) || _cultRule.AssociatedGamerule(args.Actor) is not { } cult
|| !_prototype.TryIndex(args.GlyphProtoId, out var glyph) || glyph.Tier > cult.Comp.CurrentTier)
{
ent.Comp.Target = null;
_ui.CloseUi(ent.Owner, CosmicGlyphDrawUiKey.Key, args.Actor);
return;
}
var spawned = Spawn(glyph.Entity, target);
_cultRule.TransferCultAssociation(args.Actor, spawned);
ent.Comp.Target = null;
_ui.CloseUi(ent.Owner, CosmicGlyphDrawUiKey.Key, args.Actor);
}
private List<ProtoId<GlyphPrototype>> GetAvailableGlyphs(int currentTier)
{
return _prototype.EnumeratePrototypes<GlyphPrototype>()
.Where(glyph => glyph.Tier <= currentTier)
.OrderBy(glyph => Loc.GetString(glyph.Name))
.Select(glyph => new ProtoId<GlyphPrototype>(glyph.ID))
.ToList();
}
#region Base trigger
private void OnGlyphCreated(Entity<CosmicGlyphComponent> ent, ref ComponentStartup args)

View File

@ -324,12 +324,6 @@ public sealed class MonumentSystem : SharedMonumentSystem
UpdateMonumentAppearance(uid, false);
//this is probably unnecessary but I have no idea where they get added to the list atm - ruddygreat
foreach (var glyphProto in _protoMan.EnumeratePrototypes<GlyphPrototype>().Where(proto => proto.Tier == 1))
{
uid.Comp.UnlockedGlyphs.Add(glyphProto.ID);
}
//basically completely unnecessary, but putting this here for sanity & futureproofing - ruddygreat
var query = EntityQueryEnumerator<CosmicCultComponent>();
while (query.MoveNext(out var cultist, out var cultComp))
@ -356,11 +350,6 @@ public sealed class MonumentSystem : SharedMonumentSystem
UpdateMonumentAppearance(uid, true);
foreach (var glyphProto in _protoMan.EnumeratePrototypes<GlyphPrototype>().Where(proto => proto.Tier == 2))
{
uid.Comp.UnlockedGlyphs.Add(glyphProto.ID);
}
var objectiveQuery = EntityQueryEnumerator<CosmicTierConditionComponent>();
while (objectiveQuery.MoveNext(out _, out var objectiveComp))
{
@ -398,11 +387,6 @@ public sealed class MonumentSystem : SharedMonumentSystem
if (_cosmicRule.AssociatedGamerule(uid) is not { } cult)
return;
foreach (var glyphProto in _protoMan.EnumeratePrototypes<GlyphPrototype>().Where(proto => proto.Tier == 3))
{
uid.Comp.UnlockedGlyphs.Add(glyphProto.ID);
}
UpdateMonumentAppearance(uid, true);
var objectiveQuery = EntityQueryEnumerator<CosmicTierConditionComponent>();

View File

@ -42,6 +42,7 @@ public sealed partial class CosmicCultComponent : Component
[
"ActionCosmicSiphon",
"ActionCosmicBlank",
"ActionCosmicDrawGlyph",
];
[DataField]

View File

@ -18,18 +18,6 @@ public sealed partial class MonumentComponent : Component
[DataField]
public SoundSpecifier InfusionSFX = new SoundPathSpecifier("/Audio/_DV/CosmicCult/insert_entropy.ogg");
/// <summary>
/// the list of glyphs that this monument is allowed to scribe
/// </summary>
[DataField, AutoNetworkedField]
public HashSet<ProtoId<GlyphPrototype>> UnlockedGlyphs = [];
/// <summary>
/// the glyph that will be scribed when the button is pressed
/// </summary>
[DataField, AutoNetworkedField]
public ProtoId<GlyphPrototype> SelectedGlyph;
/// <summary>
/// the total amount of entropy that has been inserted into the monument
/// </summary>
@ -120,15 +108,6 @@ public sealed class InfluenceSelectedMessage(ProtoId<InfluencePrototype> influen
public ProtoId<InfluencePrototype> InfluenceProtoId = influenceProtoId;
}
[Serializable, NetSerializable]
public sealed class GlyphSelectedMessage(ProtoId<GlyphPrototype> glyphProtoId) : BoundUserInterfaceMessage
{
public ProtoId<GlyphPrototype> GlyphProtoId = glyphProtoId;
}
[Serializable, NetSerializable]
public sealed class GlyphRemovedMessage : BoundUserInterfaceMessage;
[Serializable, NetSerializable]
public enum MonumentVisuals : byte
{

View File

@ -1,10 +1,18 @@
using Content.Shared.Actions;
using Robust.Shared.Map;
using Robust.Shared.GameStates;
namespace Content.Shared._DV.CosmicCult;
[RegisterComponent, NetworkedComponent]
public sealed partial class CosmicCultActionComponent : Component;
[RegisterComponent]
public sealed partial class CosmicGlyphDrawComponent : Component
{
public EntityCoordinates? Target;
}
public sealed partial class EventCosmicSiphon : EntityTargetActionEvent;
public sealed partial class EventCosmicBlank : EntityTargetActionEvent;
public sealed partial class EventCosmicPlaceMonument : InstantActionEvent; //given to the cult leader on roundstart
@ -16,6 +24,7 @@ public sealed partial class EventCosmicIngress : EntityTargetActionEvent;
public sealed partial class EventCosmicImposition : InstantActionEvent;
public sealed partial class EventCosmicNova : WorldTargetActionEvent;
public sealed partial class EventCosmicFragmentation : EntityTargetActionEvent;
public sealed partial class EventCosmicDrawGlyph : WorldTargetActionEvent;
// COLOSSUS ACTIONS
public sealed partial class EventCosmicColossusSunder : WorldTargetActionEvent;

View File

@ -0,0 +1,23 @@
using Content.Shared._DV.CosmicCult.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._DV.CosmicCult;
[Serializable, NetSerializable]
public enum CosmicGlyphDrawUiKey : byte
{
Key,
}
[Serializable, NetSerializable]
public sealed class CosmicGlyphDrawBuiState(List<ProtoId<GlyphPrototype>> glyphs) : BoundUserInterfaceState
{
public List<ProtoId<GlyphPrototype>> Glyphs = glyphs;
}
[Serializable, NetSerializable]
public sealed class CosmicGlyphDrawSelectedMessage(ProtoId<GlyphPrototype> glyphProtoId) : BoundUserInterfaceMessage
{
public ProtoId<GlyphPrototype> GlyphProtoId = glyphProtoId;
}

View File

@ -16,22 +16,16 @@ public sealed class MonumentBuiState : BoundUserInterfaceState
{
public int CurrentProgress;
public int TargetProgress;
public ProtoId<GlyphPrototype> SelectedGlyph;
public HashSet<ProtoId<GlyphPrototype>> UnlockedGlyphs;
public MonumentBuiState(int currentProgress, int targetProgress, int progressOffset, ProtoId<GlyphPrototype> selectedGlyph, HashSet<ProtoId<GlyphPrototype>> unlockedGlyphs)
public MonumentBuiState(int currentProgress, int targetProgress, int progressOffset)
{
CurrentProgress = currentProgress - progressOffset;
TargetProgress = targetProgress - progressOffset;
SelectedGlyph = selectedGlyph;
UnlockedGlyphs = unlockedGlyphs;
}
public MonumentBuiState(MonumentComponent comp)
{
CurrentProgress = comp.CurrentProgress - comp.ProgressOffset;
TargetProgress = comp.TargetProgress - comp.ProgressOffset;
SelectedGlyph = comp.SelectedGlyph;
UnlockedGlyphs = comp.UnlockedGlyphs;
}
}

View File

@ -1,11 +1,7 @@
using Content.Shared._DV.CosmicCult;
using Content.Shared._DV.CosmicCult.Components;
using Content.Shared._DV.CosmicCult.Prototypes;
using Content.Shared.Actions;
using Content.Shared.Movement.Components;
using Content.Shared.Nutrition.Components;
using Content.Shared.UserInterface;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Prototypes;
using Robust.Shared.Spawners;
@ -21,16 +17,12 @@ public abstract class SharedMonumentSystem : EntitySystem
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedCosmicCultSystem _cosmicCult = default!;
[Dependency] private readonly SharedCosmicGlyphSystem _glyph = default!;
[Dependency] private readonly SharedMapSystem _map = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MonumentComponent, BoundUIOpenedEvent>(OnUIOpened);
SubscribeLocalEvent<MonumentComponent, GlyphSelectedMessage>(OnGlyphSelected);
SubscribeLocalEvent<MonumentComponent, GlyphRemovedMessage>(OnGlyphRemove);
SubscribeLocalEvent<MonumentComponent, InfluenceSelectedMessage>(OnInfluenceSelected);
SubscribeLocalEvent<MonumentOnDespawnComponent, TimedDespawnEvent>(OnTimedDespawn);
SubscribeLocalEvent<MonumentCollisionComponent, PreventCollideEvent>(OnPreventCollide);
@ -80,38 +72,6 @@ public abstract class SharedMonumentSystem : EntitySystem
}
#region UI listeners
private void OnGlyphSelected(Entity<MonumentComponent> ent, ref GlyphSelectedMessage args)
{
ent.Comp.SelectedGlyph = args.GlyphProtoId;
if (!_prototype.TryIndex(args.GlyphProtoId, out var proto))
return;
var xform = Transform(ent);
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
return;
var localTile = _map.GetTileRef(xform.GridUid.Value, grid, xform.Coordinates);
var targetIndices = localTile.GridIndices + new Vector2i(0, -1);
if (ent.Comp.CurrentGlyph is { } curGlyph) _glyph.EraseGlyph(curGlyph);
var glyphEnt = Spawn(proto.Entity, _map.ToCenterCoordinates(xform.GridUid.Value, targetIndices, grid));
ent.Comp.CurrentGlyph = glyphEnt;
var evt = new CosmicCultAssociateRuleEvent(ent, glyphEnt);
RaiseLocalEvent(ref evt);
_ui.SetUiState(ent.Owner, MonumentKey.Key, new MonumentBuiState(ent.Comp));
}
private void OnGlyphRemove(Entity<MonumentComponent> ent, ref GlyphRemovedMessage args)
{
if (ent.Comp.CurrentGlyph is { } curGlyph) _glyph.EraseGlyph(curGlyph);
_ui.SetUiState(ent.Owner, MonumentKey.Key, new MonumentBuiState(ent.Comp));
}
private void OnInfluenceSelected(Entity<MonumentComponent> ent, ref InfluenceSelectedMessage args)
{
if (!_prototype.TryIndex(args.InfluenceProtoId, out var proto) || !TryComp<ActivatableUIComponent>(ent, out var uiComp) || !TryComp<CosmicCultComponent>(args.Actor, out var cultComp))

View File

@ -5,6 +5,7 @@ cult-glyph-too-many-targets = Too many targets present on glyph!
cult-glyph-target-mindshield = Mental shielding prevents the glyph's influence from taking hold!
cult-glyph-target-chaplain = A spark of divine power prevents the glyph's influence from taking hold!
cult-glyph-target-mindless = The glyph fails to activate, as the target is currently mindless.
cult-glyph-verb-erase = Erase glyph
cult-glyph-name-knowledge = Pact of Knowledge
cult-glyph-description-knowledge = Knowledge. Instills the spark of indelible knowledge. Able to convert most to join our ranks.

View File

@ -45,6 +45,28 @@
- BibleUser
event: !type:EventCosmicBlank {}
- type: entity
parent: BaseAction
id: ActionCosmicDrawGlyph
name: Draw Glyph
description: Scribe a glyph into realspace.
components:
- type: Action
useDelay: 60
itemIconStyle: NoItem
icon:
sprite: _DV/CosmicCult/Icons/cosmiccult_abilities.rsi
state: astral
- type: TargetAction
range: 4
- type: WorldTargetAction
event: !type:EventCosmicDrawGlyph {}
- type: CosmicGlyphDraw
- type: UserInterface
interfaces:
enum.CosmicGlyphDrawUiKey.Key:
type: CosmicGlyphDrawBoundUserInterface
- type: entity
parent: BaseAction
id: ActionCosmicPlaceMonument