Arcade machine improvements (#24200)

* Give 'em something to talk about

* Wire panel visuals

* Wire graphics tweak

* More ads and thanks

* More ads for a noisy arcade

* New screen for space villain machines

* Implement EmitSoundIntervalComponent and a bunch of arcade noises

* Require power for sounds

* Allow earlier startup intervals

* Orange glow

* Audio attributions

* Include the PR link

* Replace EmitSoundInterval with expanded SpamEmitSound

* Remove pacman-themed arcade sounds

* Documentation good.

* Updated methods to use Entity<T>

* Refactored SpamEmitSound to get rid of accumulator and chance.

* Fixed prewarm logic

* Moved stuff to Shared

* Fix outdated YAML

* Better prediction, auto pause handling

* Make enable/disable reset the timer instead of trying to save it.
This commit is contained in:
Tayrtahn
2024-03-28 02:28:45 -04:00
committed by GitHub
parent a071bc5dbf
commit b1ba6b5bb6
35 changed files with 425 additions and 57 deletions

View File

@@ -19,4 +19,9 @@ public sealed partial class BlockGameArcadeComponent : Component
/// The players currently viewing (but not playing) the active session of NT-BG.
/// </summary>
public readonly List<ICommonSession> Spectators = new();
/// <summary>
/// Whether the game machine should thank (or otherwise talk to) the player when they leave
/// </summary>
public bool ShouldSayThankYou;
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Power.Components;
using Content.Shared.UserInterface;
using Content.Server.Advertise;
using Content.Shared.Arcade;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
@@ -9,6 +10,7 @@ namespace Content.Server.Arcade.BlockGame;
public sealed class BlockGameArcadeSystem : EntitySystem
{
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly AdvertiseSystem _advertise = default!;
public override void Initialize()
{
@@ -89,7 +91,15 @@ public sealed class BlockGameArcadeSystem : EntitySystem
UpdatePlayerStatus(uid, component.Player, blockGame: component);
}
else
{
// Everybody's gone
component.Player = null;
if (component.ShouldSayThankYou && TryComp<AdvertiseComponent>(uid, out var advertise))
{
_advertise.SayThankYou(uid, advertise);
component.ShouldSayThankYou = false;
}
}
UpdatePlayerStatus(uid, temp, blockGame: component);
}
@@ -103,6 +113,7 @@ public sealed class BlockGameArcadeSystem : EntitySystem
_uiSystem.CloseAll(bui);
component.Player = null;
component.Spectators.Clear();
component.ShouldSayThankYou = false;
}
private void OnPlayerAction(EntityUid uid, BlockGameArcadeComponent component, BlockGameMessages.BlockGamePlayerActionMessage msg)
@@ -122,6 +133,8 @@ public sealed class BlockGameArcadeSystem : EntitySystem
return;
}
component.ShouldSayThankYou = true;
component.Game.ProcessInput(msg.PlayerAction);
}
}

View File

@@ -110,4 +110,9 @@ public sealed partial class SpaceVillainArcadeComponent : SharedSpaceVillainArca
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public int RewardAmount = 0;
/// <summary>
/// Whether the game machine should thank (or otherwise talk to) the player when they leave
/// </summary>
public bool ShouldSayThankYou;
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Power.Components;
using Content.Shared.UserInterface;
using Content.Server.Advertise;
using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
@@ -13,6 +14,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly AdvertiseSystem _advertise = default!;
public override void Initialize()
{
@@ -22,6 +24,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
SubscribeLocalEvent<SpaceVillainArcadeComponent, AfterActivatableUIOpenEvent>(OnAfterUIOpenSV);
SubscribeLocalEvent<SpaceVillainArcadeComponent, SpaceVillainArcadePlayerActionMessage>(OnSVPlayerAction);
SubscribeLocalEvent<SpaceVillainArcadeComponent, PowerChangedEvent>(OnSVillainPower);
SubscribeLocalEvent<SpaceVillainArcadeComponent, BoundUIClosedEvent>(OnBoundUIClosed);
}
/// <summary>
@@ -79,6 +82,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
case PlayerAction.Heal:
case PlayerAction.Recharge:
component.Game.ExecutePlayerAction(uid, msg.PlayerAction, component);
component.ShouldSayThankYou = true; // Any sort of gameplay action counts
break;
case PlayerAction.NewGame:
_audioSystem.PlayPvs(component.NewGameSound, uid, AudioParams.Default.WithVolume(-4f));
@@ -106,5 +110,19 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
if (_uiSystem.TryGetUi(uid, SpaceVillainArcadeUiKey.Key, out var bui))
_uiSystem.CloseAll(bui);
component.ShouldSayThankYou = false;
}
private void OnBoundUIClosed(Entity<SpaceVillainArcadeComponent> ent, ref BoundUIClosedEvent args)
{
if (args.UiKey is not SpaceVillainArcadeUiKey || (SpaceVillainArcadeUiKey) args.UiKey != SpaceVillainArcadeUiKey.Key)
return;
if (ent.Comp.ShouldSayThankYou && TryComp<AdvertiseComponent>(ent.Owner, out var advertise))
{
_advertise.SayThankYou(ent.Owner, advertise);
ent.Comp.ShouldSayThankYou = false;
}
}
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Popups;
using Content.Server.Sound.Components;
using Content.Server.Sound;
using Content.Shared.Sound.Components;
using Content.Shared.Actions;
using Content.Shared.Audio;
using Content.Shared.Bed.Sleep;
@@ -30,6 +31,7 @@ namespace Content.Server.Bed.Sleep
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
[Dependency] private readonly EmitSoundSystem _emitSound = default!;
[ValidatePrototypeId<EntityPrototype>] public const string SleepActionId = "ActionSleep";
@@ -71,8 +73,8 @@ namespace Content.Server.Bed.Sleep
{
emitSound.Sound = sleepSound.Snore;
}
emitSound.PlayChance = sleepSound.Chance;
emitSound.RollInterval = sleepSound.Interval;
emitSound.MinInterval = sleepSound.Interval;
emitSound.MaxInterval = sleepSound.MaxInterval;
emitSound.PopUp = sleepSound.PopUp;
}
@@ -128,7 +130,7 @@ namespace Content.Server.Bed.Sleep
return;
}
if (TryComp<SpamEmitSoundComponent>(uid, out var spam))
spam.Enabled = args.NewMobState == MobState.Alive;
_emitSound.SetEnabled((uid, spam), args.NewMobState == MobState.Alive);
}
private void AddWakeVerb(EntityUid uid, SleepingComponent component, GetVerbsEvent<AlternativeVerb> args)

View File

@@ -1,27 +0,0 @@
using Content.Shared.Sound.Components;
namespace Content.Server.Sound.Components
{
/// <summary>
/// Rolls to play a sound every few seconds.
/// </summary>
[RegisterComponent]
public sealed partial class SpamEmitSoundComponent : BaseEmitSoundComponent
{
[DataField("accumulator")]
public float Accumulator = 0f;
[DataField("rollInterval")]
public float RollInterval = 2f;
[DataField("playChance")]
public float PlayChance = 0.5f;
// Always Pvs.
[DataField("popUp")]
public string? PopUp;
[DataField("enabled")]
public bool Enabled = true;
}
}

View File

@@ -2,12 +2,17 @@ using Content.Server.Explosion.EntitySystems;
using Content.Server.Sound.Components;
using Content.Shared.UserInterface;
using Content.Shared.Sound;
using Robust.Shared.Random;
using Content.Shared.Sound.Components;
using Robust.Shared.Timing;
using Robust.Shared.Network;
namespace Content.Server.Sound;
public sealed class EmitSoundSystem : SharedEmitSoundSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _net = default!;
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -18,18 +23,13 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem
if (!soundSpammer.Enabled)
continue;
soundSpammer.Accumulator += frameTime;
if (soundSpammer.Accumulator < soundSpammer.RollInterval)
{
continue;
}
soundSpammer.Accumulator -= soundSpammer.RollInterval;
if (Random.Prob(soundSpammer.PlayChance))
if (_timing.CurTime >= soundSpammer.NextSound)
{
if (soundSpammer.PopUp != null)
Popup.PopupEntity(Loc.GetString(soundSpammer.PopUp), uid);
TryEmitSound(uid, soundSpammer, predict: false);
SpamEmitSoundReset((uid, soundSpammer));
}
}
}
@@ -40,6 +40,8 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem
SubscribeLocalEvent<EmitSoundOnTriggerComponent, TriggerEvent>(HandleEmitSoundOnTrigger);
SubscribeLocalEvent<EmitSoundOnUIOpenComponent, AfterActivatableUIOpenEvent>(HandleEmitSoundOnUIOpen);
SubscribeLocalEvent<SpamEmitSoundComponent, MapInitEvent>(HandleSpamEmitSoundMapInit);
}
private void HandleEmitSoundOnUIOpen(EntityUid uid, EmitSoundOnUIOpenComponent component, AfterActivatableUIOpenEvent args)
@@ -52,4 +54,39 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem
TryEmitSound(uid, component, args.User, false);
args.Handled = true;
}
private void HandleSpamEmitSoundMapInit(Entity<SpamEmitSoundComponent> entity, ref MapInitEvent args)
{
SpamEmitSoundReset(entity);
// Prewarm so multiple entities have more variation.
entity.Comp.NextSound -= Random.Next(entity.Comp.MaxInterval);
Dirty(entity);
}
private void SpamEmitSoundReset(Entity<SpamEmitSoundComponent> entity)
{
if (_net.IsClient)
return;
entity.Comp.NextSound = _timing.CurTime + ((entity.Comp.MinInterval < entity.Comp.MaxInterval)
? Random.Next(entity.Comp.MinInterval, entity.Comp.MaxInterval)
: entity.Comp.MaxInterval);
Dirty(entity);
}
public override void SetEnabled(Entity<SpamEmitSoundComponent?> entity, bool enabled)
{
if (!Resolve(entity, ref entity.Comp, false))
return;
if (entity.Comp.Enabled == enabled)
return;
entity.Comp.Enabled = enabled;
if (enabled)
SpamEmitSoundReset((entity, entity.Comp));
}
}

View File

@@ -0,0 +1,33 @@
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Sound;
using Content.Shared.Sound.Components;
namespace Content.Server.Sound;
public sealed partial class SpamEmitSoundRequirePowerSystem : SharedSpamEmitSoundRequirePowerSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpamEmitSoundRequirePowerComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<SpamEmitSoundRequirePowerComponent, PowerNetBatterySupplyEvent>(OnPowerSupply);
}
private void OnPowerChanged(Entity<SpamEmitSoundRequirePowerComponent> entity, ref PowerChangedEvent args)
{
if (TryComp<SpamEmitSoundComponent>(entity.Owner, out var comp))
{
EmitSound.SetEnabled((entity, comp), args.Powered);
}
}
private void OnPowerSupply(Entity<SpamEmitSoundRequirePowerComponent> entity, ref PowerNetBatterySupplyEvent args)
{
if (TryComp<SpamEmitSoundComponent>(entity.Owner, out var comp))
{
EmitSound.SetEnabled((entity, comp), args.Supply);
}
}
}