New glimmer events! Foxfire & Restyle (#5102)

* Add glimmer restyle and foxfire

* Fix typo

* Fix typo

* Fix test fail and kobold hair bug (thanks to FaintSpeaker)

* Update Content.Server/_DV/StationEvents/Events/GlimmerRestyleRule.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Astra <226853568+EmberAstra@users.noreply.github.com>

* Update Content.Server/_DV/StationEvents/Events/GlimmerRestyleRule.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Astra <226853568+EmberAstra@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update Content.Server/_DV/StationEvents/Components/GlimmerFoxfireSpawnRuleComponent.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Astra <226853568+EmberAstra@users.noreply.github.com>

* Update Content.Server/_DV/StationEvents/Components/GlimmerFoxfireSpawnRuleComponent.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Astra <226853568+EmberAstra@users.noreply.github.com>

* Update Content.Server/_DV/StationEvents/Events/GlimmerFoxfireSpawnRule.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Astra <226853568+EmberAstra@users.noreply.github.com>

* Update Content.Server/_DV/StationEvents/Events/GlimmerRestyleRule.cs

Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Signed-off-by: Astra <226853568+EmberAstra@users.noreply.github.com>

* Only filter for potential psionics

* Lower restyle glimmer requirement

* Semicolon my beloathed

* Popup false when going bald to bald

* Update Content.Server/_DV/StationEvents/Events/GlimmerRestyleRule.cs

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>

---------

Signed-off-by: Astra <226853568+EmberAstra@users.noreply.github.com>
Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Astra 2026-01-03 14:56:01 +01:00 committed by GitHub
parent 2e024d43cb
commit 2502a9fffe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 256 additions and 0 deletions

View File

@ -0,0 +1,43 @@
using Content.Server._DV.StationEvents.Events;
using Robust.Shared.Prototypes;
namespace Content.Server._DV.StationEvents.Components;
/// <summary>
/// Spawns a small amount of randomly-colored foxfires,
/// centered around either the Oracle or Sophic Grammateus.
/// </summary>
[RegisterComponent, Access(typeof(GlimmerFoxfireSpawnRule))]
public sealed partial class GlimmerFoxfireSpawnRuleComponent : Component
{
/// <summary>
/// Minimum+ amounts of foxfires to spawn.
/// </summary>
[DataField]
public int MinimumSpawned = 10;
/// <summary>
/// Maximum amounts of foxfires to spawn.
/// </summary>
[DataField]
public int MaximumSpawned = 20;
/// <summary>
/// Maximum distance from the Oracle or Sophic Grammateus in which the
/// foxfire will be spawned.
/// </summary>
[DataField]
public int SpawnRange = 10;
/// <summary>
/// Prototype to be spawned.
/// </summary>
[DataField]
public EntProtoId FoxfirePrototype = "Foxfire";
/// <summary>
/// Available colors for the foxfire's light.
/// </summary>
[DataField]
public List<Color>? RandomColorList = new();
}

View File

@ -0,0 +1,35 @@
using Content.Server._DV.StationEvents.Events;
namespace Content.Server._DV.StationEvents.Components;
/// <summary>
/// Attempts to change the hair and facial hair markings, plus their color
/// of a small amount of people.
/// </summary>
[RegisterComponent, Access(typeof(GlimmerRestyleRule))]
public sealed partial class GlimmerRestyleRuleComponent : Component
{
/// <summary>
/// Minimum number of valid targets that will get restyled.
/// </summary>
[DataField]
public int MinimumTargets = 1;
/// <summary>
/// Maximum number of valid targets that will get restyled.
/// </summary>
[DataField]
public int MaximumTargets = 5;
/// <summary>
/// Chance of completely removing all hair markings instead of selecting a random one.
/// </summary>
[DataField]
public float BaldChance = 0.2f;
/// <summary>
/// Chance of completely removing all facial hair markings instead of selecting a random one.
/// </summary>
[DataField]
public float CleanShavenChance = 0.5f;
}

View File

@ -0,0 +1,59 @@
using System.Numerics;
using Content.Server._DV.StationEvents.Components;
using Content.Server.Nyanotrasen.Research.SophicScribe;
using Content.Server.Research.Oracle;
using Content.Server.StationEvents.Events;
using Content.Shared.GameTicking.Components;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server._DV.StationEvents.Events;
public sealed class GlimmerFoxfireSpawnRule : StationEventSystem<GlimmerFoxfireSpawnRuleComponent>
{
[Dependency] private readonly SharedPointLightSystem _light = default!;
protected override void Started(EntityUid uid,
GlimmerFoxfireSpawnRuleComponent comp,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, comp, gameRule, args);
var locations = new List<EntityCoordinates>();
var queryScribe = EntityQueryEnumerator<SophicScribeComponent, TransformComponent>();
var queryOracle = EntityQueryEnumerator<OracleComponent, TransformComponent>();
while (queryScribe.MoveNext(out _, out var transform))
{
locations.Add(transform.Coordinates);
}
while (queryOracle.MoveNext(out _, out var transform))
{
locations.Add(transform.Coordinates);
}
if (locations.Count == 0)
return;
var selectedLocation = RobustRandom.Pick(locations);
var amountToSpawn = RobustRandom.Next(comp.MinimumSpawned, comp.MaximumSpawned);
for (var i = 0; i < amountToSpawn; i++)
{
var spawnLocation = selectedLocation.Offset(new Vector2(
RobustRandom.Next(-comp.SpawnRange, comp.SpawnRange),
RobustRandom.Next(-comp.SpawnRange, comp.SpawnRange)
));
var color = Color.GhostWhite;
if (comp.RandomColorList != null && comp.RandomColorList.Count != 0)
color = RobustRandom.Pick(comp.RandomColorList);
var fireEnt = Spawn(comp.FoxfirePrototype, spawnLocation);
_light.SetColor(fireEnt, color);
}
}
}

View File

@ -0,0 +1,76 @@
using System.Linq;
using Content.Server._DV.StationEvents.Components;
using Content.Server.Psionics;
using Content.Server.StationEvents.Events;
using Content.Shared.Abilities.Psionics;
using Content.Shared.GameTicking.Components;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Mobs.Components;
using Content.Shared.SSDIndicator;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Robust.Shared.Random;
namespace Content.Server._DV.StationEvents.Events;
public sealed class GlimmerRestyleRule : StationEventSystem<GlimmerRestyleRuleComponent>
{
[Dependency] private readonly MobStateSystem _mob = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MarkingManager _markingManager = default!;
protected override void Started(EntityUid uid, GlimmerRestyleRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, comp, gameRule, args);
var query = EntityQueryEnumerator<HumanoidAppearanceComponent, MobStateComponent>();
List<(EntityUid, HumanoidAppearanceComponent)> potentialTargets = new();
while (query.MoveNext(out var entity, out var humanoid, out var mobState))
{
if (!_mob.IsAlive(entity, mobState) || HasComp<PsionicInsulationComponent>(entity) || !HasComp<PotentialPsionicComponent>(entity))
continue;
potentialTargets.Add((entity, humanoid));
}
_random.Shuffle(potentialTargets);
var targetsToRestyle = _random.Next(comp.MinimumTargets, comp.MaximumTargets);
foreach (var (entity, humanoid) in potentialTargets)
{
if(HasComp<SSDIndicatorComponent>(entity))
continue;
if (targetsToRestyle-- <= 0)
break;
var changedHair = TryApplyRestyle((entity, humanoid), MarkingCategories.Hair, comp.BaldChance);
var changedFacialHair = TryApplyRestyle((entity, humanoid), MarkingCategories.FacialHair, comp.CleanShavenChance);
if (changedHair || changedFacialHair)
_popup.PopupEntity(Loc.GetString("glimmer-restyle-event"), entity, entity, PopupType.Medium);
Dirty(entity, humanoid);
}
}
private bool TryApplyRestyle(Entity<HumanoidAppearanceComponent> ent, MarkingCategories category, float noMarkingsChance)
{
var newMarkingColor = new Color(_random.NextFloat(), _random.NextFloat(), _random.NextFloat());
var availableMarkings = _markingManager.MarkingsByCategoryAndSpecies(category, ent.Comp.Species);
if (availableMarkings.Count == 0)
return false;
var hadCategoryBefore = ent.Comp.MarkingSet.TryGetCategory(category, out _);
ent.Comp.MarkingSet.RemoveCategory(category);
if (_random.Prob(noMarkingsChance))
return hadCategoryBefore; //Do not show the popup if you go from no markings to no markings.
var newMarking = _random.Pick(availableMarkings.Values.ToList()).AsMarking();
newMarking.SetColor(newMarkingColor);
ent.Comp.MarkingSet.AddCategory(category);
ent.Comp.MarkingSet.AddFront(category, newMarking);
return true;
}
}

View File

@ -65,6 +65,11 @@ namespace Content.Shared.Humanoid.Markings
var markingPoints = _prototypeManager.Index(speciesProto.MarkingPoints);
var res = new Dictionary<string, MarkingPrototype>();
// Begin DeltaV addition - prevents errors when category is missing
if (!markingPoints.Points.ContainsKey(category))
return res;
// End DeltaV addition
foreach (var (key, marking) in MarkingsByCategory(category))
{
if ((markingPoints.OnlyWhitelisted || markingPoints.Points[category].OnlyWhitelisted) && marking.SpeciesRestrictions == null)

View File

@ -92,3 +92,5 @@ fractured-form-nobodies = You have no alternate forms to switch to!
fractured-form-sleepy = You feel very sleepy... You should find somewhere to rest.
fractured-form-ssd = { CAPITALIZE(SUBJECT($ent)) } { CONJUGATE-BE($ent) } in a deep sleep. { CAPITALIZE(POSS-ADJ($ent)) } eyes seem to be darting around as if dreaming.
fractured-form-examine-self = You feel a strange connection to { OBJECT($ent) }.
glimmer-restyle-event = You feel like something changed about your looks...

View File

@ -17,6 +17,8 @@
#- id: LockProbers
- id: PsionicNosebleedEvent
- id: MinorMassMindSwap # Delta V
- id: GlimmerFoxfireSpawn
- id: GlimmerRestyle
- type: entity
parent: BaseGameRule
@ -197,3 +199,37 @@
- type: GlimmerEvent
minimumGlimmer: 350
- type: PsionicNosebleedRule
- type: entity
parent: BaseGlimmerEvent
id: GlimmerFoxfireSpawn
components:
- type: GlimmerEvent
minimumGlimmer: 100
maximumGlimmer: 500
- type: GlimmerFoxfireSpawnRule
randomColorList:
- aqua
- betterviolet
- blue
- chartreuse
- cyan
- deeppink
- fuchsia
- green
- indigo
- lime
- pink
- red
- silver
- yellow
- type: entity
id: GlimmerRestyle
parent: BaseGlimmerSignaturesEvent
components:
- type: GlimmerEvent
minimumGlimmer: 300
maximumGlimmer: 700
- type: GlimmerRestyleRule