2024-03-28 02:46:26 -04:00
using Content.Server.Advertise.Components ;
using Content.Server.Chat.Systems ;
using Content.Server.Power.Components ;
using Content.Shared.VendingMachines ;
using Robust.Shared.Prototypes ;
using Robust.Shared.Random ;
using Robust.Shared.Timing ;
namespace Content.Server.Advertise.EntitySystems ;
public sealed class AdvertiseSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
[Dependency] private readonly IRobustRandom _random = default ! ;
[Dependency] private readonly IGameTiming _gameTiming = default ! ;
[Dependency] private readonly ChatSystem _chat = default ! ;
/// <summary>
/// The maximum amount of time between checking if advertisements should be displayed
/// </summary>
private readonly TimeSpan _maximumNextCheckDuration = TimeSpan . FromSeconds ( 15 ) ;
/// <summary>
/// The next time the game will check if advertisements should be displayed
/// </summary>
2024-03-30 18:34:31 -07:00
private TimeSpan _nextCheckTime = TimeSpan . MinValue ;
2024-03-28 02:46:26 -04:00
public override void Initialize ( )
{
SubscribeLocalEvent < AdvertiseComponent , MapInitEvent > ( OnMapInit ) ;
2024-03-31 23:36:31 -07:00
SubscribeLocalEvent < ApcPowerReceiverComponent , AttemptAdvertiseEvent > ( OnPowerReceiverAttemptAdvertiseEvent ) ;
SubscribeLocalEvent < VendingMachineComponent , AttemptAdvertiseEvent > ( OnVendingAttemptAdvertiseEvent ) ;
2024-03-28 02:46:26 -04:00
2024-03-30 18:34:31 -07:00
_nextCheckTime = TimeSpan . MinValue ;
2024-03-28 02:46:26 -04:00
}
2024-03-31 23:36:31 -07:00
private void OnMapInit ( EntityUid uid , AdvertiseComponent advert , MapInitEvent args )
2024-03-28 02:46:26 -04:00
{
2024-04-12 02:42:20 -04:00
var prewarm = advert . Prewarm ;
RandomizeNextAdvertTime ( advert , prewarm ) ;
2024-03-31 23:36:31 -07:00
_nextCheckTime = MathHelper . Min ( advert . NextAdvertisementTime , _nextCheckTime ) ;
2024-03-28 02:46:26 -04:00
}
2024-04-12 02:42:20 -04:00
private void RandomizeNextAdvertTime ( AdvertiseComponent advert , bool prewarm = false )
2024-03-28 02:46:26 -04:00
{
2024-04-12 02:42:20 -04:00
var minDuration = prewarm ? 0 : Math . Max ( 1 , advert . MinimumWait ) ;
2024-03-31 23:36:31 -07:00
var maxDuration = Math . Max ( minDuration , advert . MaximumWait ) ;
2024-03-28 02:46:26 -04:00
var waitDuration = TimeSpan . FromSeconds ( _random . Next ( minDuration , maxDuration ) ) ;
2024-03-31 23:36:31 -07:00
advert . NextAdvertisementTime = _gameTiming . CurTime + waitDuration ;
2024-03-28 02:46:26 -04:00
}
2024-03-31 23:36:31 -07:00
public void SayAdvertisement ( EntityUid uid , AdvertiseComponent ? advert = null )
2024-03-28 02:46:26 -04:00
{
2024-03-31 23:36:31 -07:00
if ( ! Resolve ( uid , ref advert ) )
2024-03-28 02:46:26 -04:00
return ;
2024-03-31 23:36:31 -07:00
var attemptEvent = new AttemptAdvertiseEvent ( uid ) ;
RaiseLocalEvent ( uid , ref attemptEvent ) ;
2024-03-28 02:46:26 -04:00
if ( attemptEvent . Cancelled )
return ;
2024-03-31 23:36:31 -07:00
if ( _prototypeManager . TryIndex ( advert . Pack , out var advertisements ) )
2024-05-30 12:08:42 -04:00
_chat . TrySendInGameICMessage ( uid , Loc . GetString ( _random . Pick ( advertisements . Values ) ) , InGameICChatType . Speak , hideChat : true ) ;
2024-03-28 02:46:26 -04:00
}
public override void Update ( float frameTime )
{
2024-03-31 23:36:31 -07:00
var currentGameTime = _gameTiming . CurTime ;
if ( _nextCheckTime > currentGameTime )
2024-03-28 02:46:26 -04:00
return ;
2024-03-31 23:36:31 -07:00
// _nextCheckTime starts at TimeSpan.MinValue, so this has to SET the value, not just increment it.
_nextCheckTime = currentGameTime + _maximumNextCheckDuration ;
2024-03-28 02:46:26 -04:00
var query = EntityQueryEnumerator < AdvertiseComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var advert ) )
{
2024-03-31 23:36:31 -07:00
if ( currentGameTime > advert . NextAdvertisementTime )
2024-03-28 02:46:26 -04:00
{
2024-03-31 23:36:31 -07:00
SayAdvertisement ( uid , advert ) ;
// The timer is always refreshed when it expires, to prevent mass advertising (ex: all the vending machines have no power, and get it back at the same time).
RandomizeNextAdvertTime ( advert ) ;
2024-03-28 02:46:26 -04:00
}
2024-03-31 23:36:31 -07:00
_nextCheckTime = MathHelper . Min ( advert . NextAdvertisementTime , _nextCheckTime ) ;
2024-03-28 02:46:26 -04:00
}
}
2024-03-31 23:36:31 -07:00
private static void OnPowerReceiverAttemptAdvertiseEvent ( EntityUid uid , ApcPowerReceiverComponent powerReceiver , ref AttemptAdvertiseEvent args )
{
args . Cancelled | = ! powerReceiver . Powered ;
}
private static void OnVendingAttemptAdvertiseEvent ( EntityUid uid , VendingMachineComponent machine , ref AttemptAdvertiseEvent args )
{
args . Cancelled | = machine . Broken ;
}
2024-03-28 02:46:26 -04:00
}
2024-03-31 23:36:31 -07:00
[ByRefEvent]
public record struct AttemptAdvertiseEvent ( EntityUid ? Advertiser )
2024-03-28 02:46:26 -04:00
{
2024-03-31 23:36:31 -07:00
public bool Cancelled = false ;
2024-03-28 02:46:26 -04:00
}