Compare commits

..
Author SHA1 Message Date
Vanessa 7ad3187963 Add damage examinable to base structure. 2026-06-27 09:50:31 -05:00
Delta-V bot 932dd5e429 Automatic changelog update 2026-06-26 19:23:56 +02:00
wesker 3001253e43 Synthetic Emotes Fix (#6125)
* separate synthetic emote

* fix duplicate IDs (server error)

* Update speech_emotes.yml

* maybe this

* maybe keep the removed things from upstream lol

* imagine yamlops-ing when the problem was your trash code all along
2026-06-26 19:23:37 +02:00
14 changed files with 59 additions and 165 deletions
@@ -1,4 +1,3 @@
using Content.Server.RoundEnd; // DeltaV
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
@@ -26,29 +25,4 @@ public sealed partial class ZombieRuleComponent : Component
/// </summary>
[DataField]
public float ZombieShuttleCallPercentage = 0.7f;
/// <summary>
/// DeltaV - The behavior of the round if all zombies are defeated.
/// </summary>
public RoundEndBehavior ZombieRoundEndBehavior = RoundEndBehavior.ShuttleCall;
/// <summary>
/// DeltaV - The amount of time before the evac shuttle will arrive if the ZombieRoundEndBehavior is set to ShuttleCall.
/// </summary>
public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(5);
/// <summary>
/// DeltaV - The announcement sender when calling the shuttle.
/// </summary>
public LocId RoundEndTextSender = "comms-console-announcement-title-centcom";
/// <summary>
/// DeltaV - The announcement text when calling a shuttle.
/// </summary>
public LocId RoundEndTextShuttleCall = "zombie-no-more-threat-announcement-shuttle-call";
/// <summary>
/// DeltaV - The announcement text when a shuttle is already called.
/// </summary>
public LocId RoundEndTextAnnouncement = "zombie-no-more-threat-announcement";
}
@@ -30,7 +30,6 @@ using System.Linq;
using Content.Shared.Station.Components;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
using Content.Shared.Cuffs.Components; // DeltaV
namespace Content.Server.GameTicking.Rules;
@@ -501,23 +500,12 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
// Check if there are nuke operatives still alive on the same map as the shuttle,
// or on the same map as the station.
// If there are, the round can continue.
// BEGIN DeltaV - End Rounds Better
// We need to use a EntityQueryEnumerator instead of a EntityQuery, because we need the uid to check for cuffs
var operatives = new List<Entity<NukeOperativeComponent, MobStateComponent, TransformComponent>>();
var operativesEnumerator = EntityQueryEnumerator<NukeOperativeComponent, MobStateComponent, TransformComponent>();
while (operativesEnumerator.MoveNext(out var uid, out var nukeOp, out var mobState, out var transform))
{
operatives.Add((uid, nukeOp, mobState, transform));
}
var operatives = EntityQuery<NukeOperativeComponent, MobStateComponent, TransformComponent>(true);
var operativesAlive = operatives
.Where(op =>
op.Comp3.MapID == shuttleMapId
|| op.Comp3.MapID == targetStationMap)
.Any(op => op.Comp2.CurrentState == MobState.Alive && op.Comp1.Running &&
!TryComp<CuffableComponent>(op, out _)); // in the case the crew keeps one alive
// END DeltaV
op.Item3.MapID == shuttleMapId
|| op.Item3.MapID == targetStationMap)
.Any(op => op.Item2.CurrentState == MobState.Alive && op.Item1.Running);
if (operativesAlive)
return; // There are living operatives than can access the shuttle, or are still on the station's map.
@@ -3,7 +3,7 @@ using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Popups;
using Content.Server.Roles;
using Content.Server.RoundEnd; // DeltaV
using Content.Server.RoundEnd;
using Content.Server.Station.Systems;
using Content.Server.Zombies;
using Content.Shared.GameTicking.Components;
@@ -114,31 +114,11 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
/// </summary>
private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent)
{
// BEGIN DeltaV - End Rounds Better
// Zombies have failed, time to go home
if (zombieRuleComponent.ZombieRoundEndBehavior == RoundEndBehavior.Nothing)
return;
// END DeltaV
var healthy = GetHealthyHumans();
if (healthy.Count == 1) // Only one human left. spooky
_popup.PopupEntity(Loc.GetString("zombie-alone"), healthy[0], healthy[0]);
// BEGIN DeltaV - End Rounds Better
var infectedPercent = GetInfectedFraction(false);
if (Math.Round(infectedPercent, 0) == 0) // All zombies defeated
{
_roundEnd.DoRoundEndBehavior(zombieRuleComponent.ZombieRoundEndBehavior,
zombieRuleComponent.EvacShuttleTime,
zombieRuleComponent.RoundEndTextSender,
zombieRuleComponent.RoundEndTextShuttleCall,
zombieRuleComponent.RoundEndTextAnnouncement);
zombieRuleComponent.ZombieRoundEndBehavior = RoundEndBehavior.Nothing; // stop this check in the future
}
// END DeltaV
if (infectedPercent > zombieRuleComponent.ZombieShuttleCallPercentage && !_roundEnd.IsRoundEndRequested()) // DeltaV - Move GetInfectedFraction to var
if (GetInfectedFraction(false) > zombieRuleComponent.ZombieShuttleCallPercentage && !_roundEnd.IsRoundEndRequested())
{
foreach (var station in _station.GetStations())
{
+1 -1
View File
@@ -351,7 +351,7 @@ namespace Content.Server.RoundEnd
else
{
RequestRoundEnd(time, null, false, textCall,
Loc.GetString(sender), cantRecall: true);
Loc.GetString(sender));
}
break;
}
+18 -16
View File
@@ -1,6 +1,7 @@
using Content.Server.Body.Systems;
using Content.Server.Database;
using Content.Server.Chat.Systems;
using Content.Shared.Body.Components;
using Content.Shared.Chat;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Chat.TypingIndicator;
using Content.Shared.Chemistry.Reagent;
@@ -22,12 +23,15 @@ public sealed class SynthSystem : EntitySystem
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ChatSystem _chat = default!;
private EmoteSoundsPrototype? _syntheticEmoteSounds; // delta-v - proper synthe emote implementation
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SynthComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<SynthComponent, EmoteEvent>(OnEmote); // delta-v - proper synthe emote implementation
}
private void OnStartup(EntityUid uid, SynthComponent component, ComponentStartup args)
@@ -46,20 +50,18 @@ public sealed class SynthSystem : EntitySystem
}
// End DeltaV
// Begin DeltaV - Add SyntheticEmotes tag and merge synthetic emote sounds into species sounds
_tag.AddTag(uid, SyntheticEmotesTag);
if (TryComp<VocalComponent>(uid, out var vocal)
&& _proto.TryIndex(SyntheticEmoteSounds, out var synthSounds)
&& vocal.EmoteSounds is { } currentSoundsId
&& _proto.TryIndex(currentSoundsId, out var currentSounds))
{
foreach (var (emoteId, sound) in synthSounds.Sounds)
{
currentSounds.Sounds.TryAdd(emoteId, sound);
}
Dirty(uid, vocal);
}
// End DeltaV
_tag.AddTag(uid, SyntheticEmotesTag); // delta-v - proper synthe emote implementation
}
// Start DeltaV - proper synthe emote implementation
private void OnEmote(EntityUid uid, SynthComponent component, ref EmoteEvent args)
{
if (args.Handled)
return;
_syntheticEmoteSounds ??= _proto.Index(SyntheticEmoteSounds);
args.Handled = _chat.TryPlayEmoteSound(uid, _syntheticEmoteSounds, args.Emote);
}
// End DeltaV
}
@@ -61,39 +61,6 @@ public sealed partial class CosmicCultRuleComponent : Component
[DataField]
public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(5);
#region Progress Checking
/// <summary>
/// The next time the monument should check the progress of the cult.
/// </summary>
public TimeSpan NextProgressCheck = TimeSpan.Zero;
/// <summary>
/// The amount of time between progress checks.
/// </summary>
public TimeSpan TimeBetweenProgressChecks = TimeSpan.FromMinutes(15);
/// <summary>
/// The progress value of the monument last time it was checked.
/// </summary>
[DataField]
public int LastProgress = 0;
/// <summary>
/// The number of times the the progress check as failed. Resets after some progress.
/// </summary>
public int ConsecutiveProgressFails = 0;
/// <summary>
/// The number of times the progress check has to fail for the round to start to end.
/// </summary>
public int ProgressFailureTolerance = 3;
public LocId ProgressFailTextShuttleCall = "cosmiccult-progress-fail-shuttle-call";
public LocId ProgressFailTextAnnouncement = "cosmiccult-progress-fail-announcement";
#endregion
[DataField]
public HashSet<EntityUid> Cultists = [];
@@ -62,15 +62,13 @@ using Robust.Shared.Random;
using Robust.Shared.Timing;
using System.Collections.Immutable;
using System.Linq;
using Content.Shared.StatusIcon;
using Content.Shared.SSDIndicator;
namespace Content.Server._DV.CosmicCult;
/// <summary>
/// Where all the main stuff for Cosmic Cultists happens.
/// </summary>
public sealed partial class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponent>
public sealed class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRuleComponent>
{
[Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
@@ -174,28 +172,6 @@ public sealed partial class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRule
component.StewardVoteTimer = null;
StewardVote();
}
// Progress check. Is the cult trying to progress at all?
if (component.MonumentPlaced && _timing.CurTime > component.NextProgressCheck)
{
// We're going to assume one monument.
var monument = EntityQuery<MonumentComponent>().First();
if (component.LastProgress == monument.CurrentProgress)
component.ConsecutiveProgressFails++;
else
component.ConsecutiveProgressFails = 0;
// They haven't progressed enough in X checks, end the round.
if (component.ConsecutiveProgressFails >= component.ProgressFailureTolerance) {
SetWinType((uid, component), WinType.CrewMinor);
_roundEnd.DoRoundEndBehavior(component.RoundEndBehavior, component.EvacShuttleTime, component.RoundEndTextSender, component.ProgressFailTextShuttleCall, component.ProgressFailTextAnnouncement);
component.RoundEndBehavior = RoundEndBehavior.Nothing;
}
component.LastProgress = monument.CurrentProgress;
component.NextProgressCheck = _timing.CurTime + component.TimeBetweenProgressChecks;
}
if (component.ExtraRiftTimer is { } riftTimer && _timing.CurTime >= riftTimer && !component.RiftStop)
{
component.ExtraRiftTimer = _timing.CurTime + _rand.Next(TimeSpan.FromSeconds(230), TimeSpan.FromSeconds(360)); //3min50 to 6min between new rifts. Seconds instead of minutes for granularity.
@@ -466,19 +442,17 @@ public sealed partial class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRule
}
}
private int GetCultistsAlive()
private bool CultistsAlive()
{
var query = EntityQueryEnumerator<CosmicCultComponent, MobStateComponent>();
int cultistsAlive = 0;
while (query.MoveNext(out var ent, out _, out var mobComp))
{
if (TryComp<CuffableComponent>(ent, out var cuffed) && cuffed.CuffedHandCount > 0) continue;
if (TryComp<SSDIndicatorComponent>(ent, out var ssd) && ssd.IsSSD) continue; // TODO: Maybe check how long.
if (mobComp.Running && mobComp.CurrentState != MobState.Dead)
cultistsAlive++;
return true;
}
return cultistsAlive;
return false;
}
private void OnMobStateChanged(Entity<CosmicCultComponent> ent, ref MobStateChangedEvent args)
@@ -493,19 +467,14 @@ public sealed partial class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRule
private void CheckForActiveCultists()
{
// A lone cultist can't do anything, so check for more than one.
if (GetCultistsAlive() > 1)
if (CultistsAlive())
return;
var query = QueryActiveRules();
List<Entity<CosmicCultRuleComponent>> cosCultRules = new();
// ConfirmWinState needs to be outside the query, because it might add an active rule
while (query.MoveNext(out var ruleUid, out _, out var ruleComp, out _))
{
cosCultRules.Add((ruleUid, ruleComp));
ConfirmWinState((ruleUid, ruleComp));
}
cosCultRules.ForEach(ConfirmWinState);
}
private void ConfirmWinState(Entity<CosmicCultRuleComponent> ent)
@@ -535,6 +504,8 @@ public sealed partial class CosmicCultRuleSystem : GameRuleSystem<CosmicCultRule
else
SetWinType(ent, WinType.Neutral); //The monument wasn't completed, no cultists escaped to midpoint. Some cultists still remain on the station, though.
if (CultistsAlive()) return; //If there are no cultists alive, ignore all previous checks, crew alreay won.
if (tier <= 1) //Prevent the cult getting cooked by accident before anyone even knows there's a cult.
{
SetWinType(ent, WinType.CrewMinor); //If we somehow get here at the end of the round, we'll still count this as a crew minor.
+7 -7
View File
@@ -1,11 +1,4 @@
Entries:
- author: marigauge
changes:
- message: Cybernetic eyes are no longer edible.
type: Fix
id: 1964
time: '2025-12-16T15:08:42.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4967
- author: ShepardToTheStars
changes:
- message: Brains are no longer edible.
@@ -4417,4 +4410,11 @@
id: 2464
time: '2026-06-25T17:59:50.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6054
- author: wesk
changes:
- message: Fixed synthetics from giving everyone borg emotes on round-start.
type: Fix
id: 2465
time: '2026-06-26T17:23:37.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/6125
Order: 1
@@ -62,11 +62,9 @@ cosmiccult-summary-crewminor = The Monument did not reach full power.
cosmiccult-summary-crewmajor = All cosmic cultists were eliminated.
cosmiccult-summary-crewcomplete = All cosmic cultists were deconverted!
cosmiccult-elimination-shuttle-call = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle has been automatically called to the station for decontamination and debriefing procedures. ETA: {$time} {$units}.
cosmiccult-elimination-announcement = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle is already inbound. Return to Midpoint safely for decontamination and debriefing procedures.
cosmiccult-elimination-shuttle-call = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle has been automatically called to the station for decontamination and debriefing procedures. ETA: {$time} {$units}. Please note, if the psychological impact of the anomaly is negligible, you may recall the shuttle to extend the shift.
cosmiccult-elimination-announcement = Based on scans from our long-range sensors, the Λ-CDM anomaly has subsided. We thank you for your prudence. An emergency shuttle is already inbound. Return to CentComm safely for decontamination and debriefing procedures.
cosmiccult-progress-fail-shuttle-call = Due to sustained Λ-CDM activity on you station, an emergency shuttle has been automatically called so that the station may be decontaminated as soon as possible. We thank you for your cooperation.
cosmiccult-progress-fail-announcement = Due to sustained Λ-CDM activity on you station, an emergency shuttle is already inbound. Please ensure as much of the station is ready and accessible for the decontamination team. We thank you for your cooperation.
## BRIEFINGS
@@ -0,0 +1,7 @@
# Base structure damage examine values
comp-structure-damaged-1 = It looks fully intact.
comp-structure-damaged-2 = It has a few scratches.
comp-structure-damaged-3 = It has a few large dents.
comp-structure-damaged-4 = [color=yellow]It has several big cracks running along its surface.[/color]
comp-structure-damaged-5 = [color=orange]It has deep cracks across multiple layers.[/color]
comp-structure-damaged-6 = [color=red]It's extremely damaged and on the verge of falling apart.[/color]
@@ -1,2 +0,0 @@
zombie-no-more-threat-announcement-shuttle-call = Long-range sensors indicate that you station has dealt with a biohazardous threat involving Romerol. Station decontamination required. The emergency shuttle has been called. ETA: {$time} {$units}.
zombie-no-more-threat-announcement = Long-range sensors indicate that you station has dealt with a biohazardous threat involving Romerol. Station decontamination required. The emergency shuttle is already en route.
+1 -1
View File
@@ -1,2 +1,2 @@
nuke-ops-no-more-threat-announcement-shuttle-call = Long-range sensors indicate the nuclear threat has been eliminated. The emergency shuttle has been called. ETA: {$time} {$units}.
nuke-ops-no-more-threat-announcement-shuttle-call = Long-range sensors indicate the nuclear threat has been eliminated. The emergency shuttle has been called. ETA: {$time} {$units}. You may recall the shuttle to extend the shift.
nuke-ops-no-more-threat-announcement = Long-range sensors indicate the nuclear threat has been eliminated. The emergency shuttle is already en route.
@@ -26,6 +26,10 @@
- type: Tag
tags:
- Structure
# BEGIN DeltaV
- type: ExaminableDamage
messages: BaseStructureMessages
# END DeltaV
- type: entity
# This means that it's not anchored on spawn.
@@ -0,0 +1,5 @@
- type: localizedDataset
id: BaseStructureMessages
values:
prefix: comp-structure-damaged-
count: 6