+++ /dev/null
-using Content.Server.Advertise.EntitySystems;
-using Content.Shared.Dataset;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Advertise.Components;
-
-/// <summary>
-/// Makes this entity periodically advertise by speaking a randomly selected
-/// message from a specified dataset into local chat.
-/// </summary>
-[RegisterComponent, Access(typeof(AdvertiseSystem))]
-public sealed partial class AdvertiseComponent : Component
-{
- /// <summary>
- /// Minimum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal to 1.
- /// </summary>
- [DataField]
- public int MinimumWait { get; private set; } = 8 * 60;
-
- /// <summary>
- /// Maximum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal
- /// to <see cref="MinimumWait"/>
- /// </summary>
- [DataField]
- public int MaximumWait { get; private set; } = 10 * 60;
-
- /// <summary>
- /// If true, the delay before the first advertisement (at MapInit) will ignore <see cref="MinimumWait"/>
- /// and instead be rolled between 0 and <see cref="MaximumWait"/>. This only applies to the initial delay;
- /// <see cref="MinimumWait"/> will be respected after that.
- /// </summary>
- [DataField]
- public bool Prewarm = true;
-
- /// <summary>
- /// The identifier for the advertisements dataset prototype.
- /// </summary>
- [DataField(required: true)]
- public ProtoId<LocalizedDatasetPrototype> Pack { get; private set; }
-
- /// <summary>
- /// The next time an advertisement will be said.
- /// </summary>
- [DataField]
- public TimeSpan NextAdvertisementTime { get; set; } = TimeSpan.Zero;
-
-}
+++ /dev/null
-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>
- private TimeSpan _nextCheckTime = TimeSpan.MinValue;
-
- public override void Initialize()
- {
- SubscribeLocalEvent<AdvertiseComponent, MapInitEvent>(OnMapInit);
-
- SubscribeLocalEvent<ApcPowerReceiverComponent, AttemptAdvertiseEvent>(OnPowerReceiverAttemptAdvertiseEvent);
- SubscribeLocalEvent<VendingMachineComponent, AttemptAdvertiseEvent>(OnVendingAttemptAdvertiseEvent);
-
- _nextCheckTime = TimeSpan.MinValue;
- }
-
- private void OnMapInit(EntityUid uid, AdvertiseComponent advert, MapInitEvent args)
- {
- var prewarm = advert.Prewarm;
- RandomizeNextAdvertTime(advert, prewarm);
- _nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime);
- }
-
- private void RandomizeNextAdvertTime(AdvertiseComponent advert, bool prewarm = false)
- {
- var minDuration = prewarm ? 0 : Math.Max(1, advert.MinimumWait);
- var maxDuration = Math.Max(minDuration, advert.MaximumWait);
- var waitDuration = TimeSpan.FromSeconds(_random.Next(minDuration, maxDuration));
-
- advert.NextAdvertisementTime = _gameTiming.CurTime + waitDuration;
- }
-
- public void SayAdvertisement(EntityUid uid, AdvertiseComponent? advert = null)
- {
- if (!Resolve(uid, ref advert))
- return;
-
- var attemptEvent = new AttemptAdvertiseEvent(uid);
- RaiseLocalEvent(uid, ref attemptEvent);
- if (attemptEvent.Cancelled)
- return;
-
- if (_prototypeManager.TryIndex(advert.Pack, out var advertisements))
- _chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Values)), InGameICChatType.Speak, hideChat: true);
- }
-
- public override void Update(float frameTime)
- {
- var currentGameTime = _gameTiming.CurTime;
- if (_nextCheckTime > currentGameTime)
- return;
-
- // _nextCheckTime starts at TimeSpan.MinValue, so this has to SET the value, not just increment it.
- _nextCheckTime = currentGameTime + _maximumNextCheckDuration;
-
- var query = EntityQueryEnumerator<AdvertiseComponent>();
- while (query.MoveNext(out var uid, out var advert))
- {
- if (currentGameTime > advert.NextAdvertisementTime)
- {
- 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);
- }
- _nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime);
- }
- }
-
-
- 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;
- }
-}
-
-[ByRefEvent]
-public record struct AttemptAdvertiseEvent(EntityUid? Advertiser)
-{
- public bool Cancelled = false;
-}
using Content.Server.Emp;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
+using Content.Server.Vocalization.Systems;
using Content.Shared.Cargo;
using Content.Shared.Damage;
using Content.Shared.Destructible;
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamageChanged);
SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice);
SubscribeLocalEvent<VendingMachineComponent, EmpPulseEvent>(OnEmpPulse);
+ SubscribeLocalEvent<VendingMachineComponent, TryVocalizeEvent>(OnTryVocalize);
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
component.NextEmpEject = _timing.CurTime;
}
}
+
+ private void OnTryVocalize(Entity<VendingMachineComponent> ent, ref TryVocalizeEvent args)
+ {
+ args.Cancelled |= ent.Comp.Broken;
+ }
}
}
--- /dev/null
+using Content.Shared.Dataset;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Vocalization.Components;
+
+/// <summary>
+/// A simple message provider for <see cref="VocalizationSystem"/> that randomly selects
+/// messages from a <see cref="LocalizedDatasetPrototype"/>.
+/// </summary>
+[RegisterComponent]
+public sealed partial class DatasetVocalizerComponent : Component
+{
+ /// <summary>
+ /// ID of the <see cref="LocalizedDatasetPrototype"/> that will provide messages.
+ /// </summary>
+ [DataField]
+ public ProtoId<LocalizedDatasetPrototype> Dataset;
+}
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan NextVocalizeInterval = TimeSpan.Zero;
+
+ /// <summary>
+ /// If true, messages spoken by this vocalizer will not be logged in the chat window
+ /// and will only be shown as speech bubbles.
+ /// </summary>
+ [DataField]
+ public bool HideChat;
}
--- /dev/null
+namespace Content.Server.Vocalization.Components;
+
+/// <summary>
+/// Used in combination with <see cref="VocalizerComponent"/>.
+/// Blocks any attempts to vocalize if the entity has an <see cref="ApcPowerReceiverComponent"/>
+/// and is currently unpowered.
+/// </summary>
+[RegisterComponent]
+public sealed partial class VocalizerRequiresPowerComponent : Component;
--- /dev/null
+using Content.Server.Vocalization.Components;
+using Content.Shared.Random.Helpers;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Server.Vocalization.Systems;
+
+/// <inheritdoc cref="DatasetVocalizerComponent"/>
+public sealed class DatasetVocalizationSystem : EntitySystem
+{
+ [Dependency] private readonly IPrototypeManager _protoMan = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<DatasetVocalizerComponent, TryVocalizeEvent>(OnTryVocalize);
+ }
+
+ private void OnTryVocalize(Entity<DatasetVocalizerComponent> ent, ref TryVocalizeEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ var dataset = _protoMan.Index(ent.Comp.Dataset);
+
+ args.Message = _random.Pick(dataset);
+ args.Handled = true;
+ }
+}
using Content.Server.Chat.Systems;
+using Content.Server.Power.Components;
using Content.Server.Vocalization.Components;
using Content.Shared.ActionBlocker;
using Robust.Shared.Random;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IRobustRandom _random = default!;
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<VocalizerComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<VocalizerRequiresPowerComponent, TryVocalizeEvent>(OnRequiresPowerTryVocalize);
+ }
+
+ private void OnMapInit(Entity<VocalizerComponent> ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextVocalizeInterval = _random.Next(ent.Comp.MinVocalizeInterval, ent.Comp.MaxVocalizeInterval);
+ }
+
+ private void OnRequiresPowerTryVocalize(Entity<VocalizerRequiresPowerComponent> ent, ref TryVocalizeEvent args)
+ {
+ if (!TryComp<ApcPowerReceiverComponent>(ent, out var receiver))
+ return;
+
+ args.Cancelled |= !receiver.Powered;
+ }
+
/// <summary>
/// Try speaking by raising a TryVocalizeEvent
/// This event is passed to systems adding a message to it and setting it to handled
var tryVocalizeEvent = new TryVocalizeEvent();
RaiseLocalEvent(entity.Owner, ref tryVocalizeEvent);
+ // If the event was cancelled, don't speak
+ if (tryVocalizeEvent.Cancelled)
+ return;
+
// if the event was never handled, return
// this happens if there are no components that trigger systems to add a message to this event
if (!tryVocalizeEvent.Handled)
return;
// send the message
- _chat.TrySendInGameICMessage(entity, message, InGameICChatType.Speak, ChatTransmitRange.Normal);
+ _chat.TrySendInGameICMessage(entity, message, InGameICChatType.Speak, entity.Comp.HideChat ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal);
}
public override void Update(float frameTime)
/// <param name="Message">Message to send, this is null when the event is just fired and should be set by a system</param>
/// <param name="Handled">Whether the message was handled by a system</param>
[ByRefEvent]
-public record struct TryVocalizeEvent(string? Message = null, bool Handled = false);
+public record struct TryVocalizeEvent(string? Message = null, bool Handled = false, bool Cancelled = false);
/// <summary>
/// Fired when the entity wants to vocalize and has a message. Allows for interception by other systems if the
- type: Transform
pos: 8.5,27.5
parent: 2
- - type: Advertise
- nextAdvertisementTime: 6277.3942716
- proto: WallPlastitaniumDiagonalIndestructible
entities:
- uid: 171
interactFailureString: petting-failure-firebot
interactSuccessSound:
path: /Audio/Ambience/Objects/periodic_beep.ogg
- - type: Advertise
- pack: FirebotAd
+ - type: Vocalizer
+ - type: DatasetVocalizer
+ dataset: FirebotAd
- type: entity
parent: MobSiliconBase
interactFailureString: petting-failure-medibot
interactSuccessSound:
path: /Audio/Ambience/Objects/periodic_beep.ogg
- - type: Advertise
- pack: MedibotAds
+ - type: Vocalizer
+ - type: DatasetVocalizer
+ dataset: MedibotAds
- type: Inventory
templateId: medibot
- type: DoAfter
type: WiresBoundUserInterface
- type: Computer
board: SpaceVillainArcadeComputerCircuitboard
- - type: Advertise
- pack: SpaceVillainAds
- minimumWait: 60 # Arcades are noisy
- maximumWait: 240
+ - type: Vocalizer
+ minVocalizeInterval: 1m # Arcades are noisy
+ maxVocalizeInterval: 4m
+ hideChat: true
+ - type: DatasetVocalizer
+ dataset: SpaceVillainAds
- type: SpeakOnUIClosed
pack: SpaceVillainGoodbyes
type: WiresBoundUserInterface
- type: Computer
board: BlockGameArcadeComputerCircuitboard
- - type: Advertise
- pack: BlockGameAds
- minimumWait: 60 # Arcades are noisy
- maximumWait: 240
+ - type: Vocalizer
+ minVocalizeInterval: 1m # Arcades are noisy
+ maxVocalizeInterval: 4m
+ hideChat: true
+ - type: DatasetVocalizer
+ dataset: BlockGameAds
- type: SpeakOnUIClosed
pack: BlockGameGoodbyes
- type: Appearance
- type: Speech
speechVerb: Robotic
- - type: Advertise
- pack: FatExtractorFacts
+ - type: Vocalizer
+ hideChat: true
+ - type: VocalizerRequiresPower
+ - type: DatasetVocalizer
+ dataset: FatExtractorFacts
- type: StaticPrice
price: 1000
- type: ResistLocker
description: A refrigerated storage unit for keeping items cold and fresh.
components:
- type: StationAiWhitelist
- - type: Advertise
- pack: SmartFridgeAds
+ - type: Vocalizer
+ hideChat: true
+ - type: VocalizerRequiresPower
+ - type: DatasetVocalizer
+ dataset: SmartFridgeAds
- type: Speech
- type: Appearance
- type: Sprite
- type: Speech
speechVerb: Robotic
speechSounds: Vending
+ - type: Vocalizer
+ minVocalizeInterval: 8m
+ maxVocalizeInterval: 10m
+ hideChat: true
+ - type: VocalizerRequiresPower
- type: SpookySpeaker
messageSet: SpookySpeakerMessagesGeneric
speakChance: 0.2
layer:
- MachineLayer
density: 190
- - type: Advertise
- pack: CondimentVendAds
+ - type: DatasetVocalizer
+ dataset: CondimentVendAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Transform
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: AmmoVendAds
+ - type: DatasetVocalizer
+ dataset: CondimentVendAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
normalState: normal-unshaded
denyState: deny-unshaded
loopDeny: false
- - type: Advertise
- pack: BoozeOMatAds
+ - type: DatasetVocalizer
+ dataset: BoozeOMatAds
- type: SpeakOnUIClosed
pack: BoozeOMatGoodbyes
- type: Sprite
normalState: normal-unshaded
denyState: deny-unshaded
loopDeny: false
- - type: Advertise
- pack: BruiseOMatAds
+ - type: DatasetVocalizer
+ dataset: BruiseOMatAds
- type: SpeakOnUIClosed
pack: BruiseOMatGoodbyes
- type: Sprite
brokenState: broken
normalState: normal-unshaded
ejectState: eject-unshaded
- - type: Advertise
- pack: ChefvendAds
+ - type: DatasetVocalizer
+ dataset: ChefvendAds
- type: SpeakOnUIClosed
pack: ChefvendGoodbyes
- type: Sprite
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: CigaretteMachineAds
+ - type: DatasetVocalizer
+ dataset: CigaretteMachineAds
- type: SpeakOnUIClosed
pack: CigaretteMachineGoodbyes
- type: Sprite
brokenState: broken
normalState: normal-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: ClothesMateAds
+ - type: DatasetVocalizer
+ dataset: ClothesMateAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
brokenState: broken
normalState: normal-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: ClothesMateAds
+ - type: DatasetVocalizer
+ dataset: ClothesMateAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
ejectDelay: 5
soundVend: /Audio/Machines/machine_vend_hot_drink.ogg
initialStockQuality: 0.33
- - type: Advertise
- pack: HotDrinksMachineAds
+ - type: DatasetVocalizer
+ dataset: HotDrinksMachineAds
- type: SpeakOnUIClosed
pack: HotDrinksMachineGoodbyes
- type: Sprite
denyState: deny-unshaded
ejectDelay: 1.9
initialStockQuality: 0.33
- - type: Advertise
- pack: RobustSoftdrinksAds
+ - type: DatasetVocalizer
+ dataset: RobustSoftdrinksAds
- type: SpeakOnUIClosed
pack: RobustSoftdrinksGoodbyes
- type: Sprite
denyState: deny-unshaded
ejectDelay: 1.9
initialStockQuality: 0.33
- - type: Advertise
- pack: RobustSoftdrinksAds
+ - type: DatasetVocalizer
+ dataset: RobustSoftdrinksAds
- type: Sprite
sprite: Structures/Machines/VendingMachines/shamblersjuice.rsi
layers:
denyState: deny-unshaded
ejectDelay: 1.9
initialStockQuality: 0.33
- - type: Advertise
- pack: RobustSoftdrinksAds
+ - type: DatasetVocalizer
+ dataset: RobustSoftdrinksAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
denyState: deny-unshaded
ejectDelay: 1.9
initialStockQuality: 0.33
- - type: Advertise
- pack: DrGibbAds
+ - type: DatasetVocalizer
+ dataset: DrGibbAds
- type: SpeakOnUIClosed
pack: DrGibbGoodbyes
- type: Sprite
denyState: deny-unshaded
ejectDelay: 1.9
initialStockQuality: 0.33
- - type: Advertise
- pack: SmiteAds
+ - type: DatasetVocalizer
+ dataset: SmiteAds
- type: SpeakOnUIClosed
pack: SmiteGoodbyes
- type: PointLight
brokenState: broken
normalState: normal-unshaded
ejectState: eject-unshaded
- - type: Advertise
- pack: DinnerwareAds
+ - type: DatasetVocalizer
+ dataset: DinnerwareAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: MagiVendAds
+ - type: DatasetVocalizer
+ dataset: MagiVendAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
brokenState: broken
normalState: normal-unshaded
initialStockQuality: 0.33
- - type: Advertise
- pack: DiscountDansAds
+ - type: DatasetVocalizer
+ dataset: DiscountDansAds
- type: SpeakOnUIClosed
pack: DiscountDansGoodbyes
- type: Sprite
ejectState: eject-unshaded
denyState: deny-unshaded
ejectDelay: 0.6
- - type: Advertise
- pack: NanoMedAds
+ - type: DatasetVocalizer
+ dataset: NanoMedAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: NutriMaxAds
+ - type: DatasetVocalizer
+ dataset: NutriMaxAds
- type: SpeakOnUIClosed
pack: NutriMaxGoodbyes
- type: Sprite
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: SecTechAds
+ - type: DatasetVocalizer
+ dataset: SecTechAds
- type: SpeakOnUIClosed
pack: SecTechGoodbyes
- type: Sprite
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: MegaSeedAds
+ - type: DatasetVocalizer
+ dataset: MegaSeedAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
ejectState: eject-unshaded
denyState: deny-unshaded
initialStockQuality: 0.33
- - type: Advertise
- pack: GetmoreChocolateCorpAds
+ - type: DatasetVocalizer
+ dataset: GetmoreChocolateCorpAds
- type: SpeakOnUIClosed
pack: GetmoreChocolateCorpGoodbyes
- type: Sprite
ejectState: eject-unshaded
denyState: deny-unshaded
initialStockQuality: 0.33
- - type: Advertise
- pack: BodaAds
+ - type: DatasetVocalizer
+ dataset: BodaAds
- type: SpeakOnUIClosed
pack: BodaGoodbyes
- type: Sprite
ejectState: eject-unshaded
denyState: deny-unshaded
screenState: screen
- - type: Advertise
- pack: AutoDrobeAds
+ - type: DatasetVocalizer
+ dataset: AutoDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: VendomatAds
+ - type: DatasetVocalizer
+ dataset: VendomatAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
normalState: normal-unshaded
ejectState: eject-unshaded
denyState: deny-unshaded
- - type: Advertise
- pack: VendomatAds
+ - type: DatasetVocalizer
+ dataset: VendomatAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
normalState: normal-unshaded
ejectState: eject-unshaded
ejectDelay: 1.8
- - type: Advertise
- pack: GoodCleanFunAds
+ - type: DatasetVocalizer
+ dataset: GoodCleanFunAds
- type: SpeakOnUIClosed
pack: GoodCleanFunGoodbyes
- type: Sprite
brokenState: broken
normalState: normal-unshaded
initialStockQuality: 0.33
- - type: Advertise
- pack: ChangAds
+ - type: DatasetVocalizer
+ dataset: ChangAds
- type: SpeakOnUIClosed
pack: ChangGoodbyes
- type: Sprite
brokenState: broken
normalState: normal-unshaded
initialStockQuality: 0.33
- - type: Advertise
- pack: DonutAds
+ - type: DatasetVocalizer
+ dataset: DonutAds
- type: SpeakOnUIClosed
pack: DonutGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: HyDrobeAds
+ - type: DatasetVocalizer
+ dataset: HyDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: LawDrobeAds
+ - type: DatasetVocalizer
+ dataset: LawDrobeAds
- type: SpeakOnUIClosed
pack: LawDrobeGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: SecDrobeAds
+ - type: DatasetVocalizer
+ dataset: SecDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: BarDrobeAds
+ - type: DatasetVocalizer
+ dataset: BarDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: CargoDrobeAds
+ - type: DatasetVocalizer
+ dataset: CargoDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: MediDrobeAds
+ - type: DatasetVocalizer
+ dataset: MediDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: ChemDrobeAds
+ - type: DatasetVocalizer
+ dataset: ChemDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: CuraDrobeAds
+ - type: DatasetVocalizer
+ dataset: CuraDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: AtmosDrobeAds
+ - type: DatasetVocalizer
+ dataset: AtmosDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: EngiDrobeAds
+ - type: DatasetVocalizer
+ dataset: EngiDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: ChefDrobeAds
+ - type: DatasetVocalizer
+ dataset: ChefDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: DetDrobeAds
+ - type: DatasetVocalizer
+ dataset: DetDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: JaniDrobeAds
+ - type: DatasetVocalizer
+ dataset: JaniDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: SciDrobeAds
+ - type: DatasetVocalizer
+ dataset: SciDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: SyndieDrobeAds
+ - type: DatasetVocalizer
+ dataset: SyndieDrobeAds
- type: SpeakOnUIClosed
pack: SyndieDrobeGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: RoboDrobeAds
+ - type: DatasetVocalizer
+ dataset: RoboDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: GeneDrobeAds
+ - type: DatasetVocalizer
+ dataset: GeneDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: ViroDrobeAds
+ - type: DatasetVocalizer
+ dataset: ViroDrobeAds
- type: SpeakOnUIClosed
pack: GenericVendGoodbyes
- type: Sprite
radius: 1.5
energy: 1.6
color: "#3c5eb5"
- - type: Advertise
- pack: HappyHonkAds
+ - type: DatasetVocalizer
+ dataset: HappyHonkAds
- type: SpeakOnUIClosed
pack: HappyHonkGoodbyes
- type: AccessReader
offState: off
brokenState: broken
normalState: normal-unshaded
- - type: Advertise
- pack: PrideDrobeAds
+ - type: DatasetVocalizer
+ dataset: PrideDrobeAds
- type: SpeakOnUIClosed
pack: PrideDrobeGoodbyes
- type: Speech