This commit is contained in:
Astra 2026-08-14 01:46:09 +00:00 committed by GitHub
commit 0c37137412
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 552 additions and 15 deletions

View File

@ -1,3 +1,4 @@
using Content.Shared._Goobstation.Loudspeaker.Events; // goob - loudspeakers
using System.Globalization;
using System.Linq;
using System.Text;
@ -62,7 +63,7 @@ public sealed partial class ChatSystem : SharedChatSystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ReplacementAccentSystem _wordreplacement = default!;
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
//Nyano - Summary: pulls in the nyano chat system for psionics.
[Dependency] private readonly NyanoChatSystem _nyanoChatSystem = default!;
@ -445,11 +446,33 @@ public sealed partial class ChatSystem : SharedChatSystem
name = FormattedMessage.EscapeText(name);
// goob start - loudspeakers
int? loudSpeakFont = null;
var getLoudspeakerEv = new GetLoudspeakerEvent();
RaiseLocalEvent(source, ref getLoudspeakerEv);
if (getLoudspeakerEv.Loudspeakers != null)
foreach (var loudspeaker in getLoudspeakerEv.Loudspeakers)
{
var loudSpeakerEv = new GetLoudspeakerDataEvent();
RaiseLocalEvent(loudspeaker, ref loudSpeakerEv);
if (loudSpeakerEv.IsActive && loudSpeakerEv.AffectChat)
{
loudSpeakFont = loudSpeakerEv.FontSize;
break;
}
}
// goob end
var wrappedMessage = Loc.GetString(speech.Bold ? "chat-manager-entity-say-bold-wrap-message" : "chat-manager-entity-say-wrap-message",
("entityName", name),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
("fontSize", loudSpeakFont ?? speech.FontSize),
("message", FormattedMessage.EscapeText(message)));
SendInVoiceRange(ChatChannel.Local, message, wrappedMessage, source, range);

View File

@ -1,3 +1,4 @@
using Content.Shared._Goobstation.Loudspeaker.Events; // goob - loudspeakers
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
@ -125,6 +126,28 @@ public sealed class RadioSystem : EntitySystem
? FormattedMessage.EscapeText(message)
: message;
// goob start - loudspeakers
int? loudSpeakFont = null;
var getLoudspeakerEv = new GetLoudspeakerEvent();
RaiseLocalEvent(messageSource, ref getLoudspeakerEv);
if (getLoudspeakerEv.Loudspeakers != null)
foreach (var loudspeaker in getLoudspeakerEv.Loudspeakers)
{
var loudSpeakerEv = new GetLoudspeakerDataEvent();
RaiseLocalEvent(loudspeaker, ref loudSpeakerEv);
if (loudSpeakerEv.IsActive && loudSpeakerEv.AffectRadio)
{
loudSpeakFont = loudSpeakerEv.FontSize;
break;
}
}
// goob end
// DeltaV - This change is to change up how the messages are wrapped up. Basically changing the formatting depending on the emote type.
string wrappedMessage;
@ -144,7 +167,7 @@ public sealed class RadioSystem : EntitySystem
wrappedMessage = Loc.GetString(speech.Bold ? "chat-radio-message-wrap-bold" : "chat-radio-message-wrap",
("color", channel.Color),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
("fontSize", loudSpeakFont ?? speech.FontSize),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("channel", $"\\[{channel.LocalizedName}\\]"),
("name", name),

View File

@ -1,3 +1,4 @@
using Content.Shared._Goobstation.Speech;
using Content.Shared.Chat;
using Content.Shared.Speech;
using Robust.Shared.Audio;
@ -24,12 +25,27 @@ namespace Content.Server.Speech
public SoundSpecifier? GetSpeechSound(Entity<SpeechComponent> ent, string message)
{
if (ent.Comp.SpeechSounds == null)
return null;
// Goobstation start
var getSpeechSoundEv = new GetSpeechSoundEvent();
RaiseLocalEvent(ent, ref getSpeechSoundEv);
SpeechSoundsPrototype? prototype;
if (getSpeechSoundEv.Handled)
{
if (getSpeechSoundEv.SpeechSoundProtoId is not { } protoId ||
!_protoManager.TryIndex(protoId, out prototype))
return null;
}
else
{
if (ent.Comp.SpeechSounds == null)
return null;
prototype = _protoManager.Index<SpeechSoundsPrototype>(ent.Comp.SpeechSounds);
}
// Goobstation end
// Play speech sound
SoundSpecifier? contextSound;
var prototype = _protoManager.Index<SpeechSoundsPrototype>(ent.Comp.SpeechSounds);
// Different sounds for ask/exclaim based on last character
contextSound = message[^1] switch

View File

@ -0,0 +1,68 @@
using Content.Shared.Inventory;
using Content.Shared.Speech;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Goobstation.Loudspeaker.Components;
/// <summary>
/// Used for items (or entities) that have loudspeaker capabilities.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class LoudspeakerComponent : Component
{
/// <summary>
/// Should it work in your hands?
/// </summary>
[DataField, AutoNetworkedField]
public bool WorksInHand;
/// <summary>
/// Can it be toggled on/off?
/// </summary>
[DataField, AutoNetworkedField]
public bool CanToggle;
/// <summary>
/// Is the loudspeaker active?
/// </summary>
[DataField, AutoNetworkedField]
public bool IsActive;
/// <summary>
/// Should it affect regular speaking?
/// </summary>
[DataField, AutoNetworkedField]
public bool AffectChat;
/// <summary>
/// Should it affect speaking via radio?
/// </summary>
[DataField, AutoNetworkedField]
public bool AffectRadio;
/// <summary>
/// How big should the new text font be?
/// </summary>
[DataField, AutoNetworkedField]
public int FontSize = 18;
/// <summary>
/// The slot it should take up to work.
/// </summary>
[DataField]
public SlotFlags RequiredSlot = SlotFlags.EARS;
/// <summary>
/// The sound it should play for the user when toggling.
/// </summary>
[DataField]
public SoundPathSpecifier ToggleSound = new("/Audio/Items/pen_click.ogg");
/// <summary>
/// The sounds the user will make when speaking.
/// </summary>
[DataField]
public ProtoId<SpeechSoundsPrototype>? SpeechSounds;
}

View File

@ -0,0 +1,11 @@
namespace Content.Shared._Goobstation.Loudspeaker.Components;
/// <summary>
/// Marks an entity that is holding equipped loudspeaker(s).
/// </summary>
[RegisterComponent]
public sealed partial class LoudspeakerHolderComponent : Component
{
[DataField]
public List<EntityUid> Loudspeakers = new();
}

View File

@ -0,0 +1,12 @@
using Content.Shared.Speech;
using Robust.Shared.Prototypes;
namespace Content.Shared._Goobstation.Loudspeaker.Events;
[ByRefEvent]
public record struct GetLoudspeakerDataEvent(
bool IsActive = false,
int? FontSize = null,
bool AffectRadio = false,
bool AffectChat = false,
ProtoId<SpeechSoundsPrototype>? SpeechSounds = null);

View File

@ -0,0 +1,5 @@
namespace Content.Shared._Goobstation.Loudspeaker.Events;
[ByRefEvent]
public record struct GetLoudspeakerEvent(
List<EntityUid> Loudspeakers);

View File

@ -0,0 +1,161 @@
using Content.Shared._Goobstation.Loudspeaker.Events;
using Content.Shared._Goobstation.Speech;
using Content.Shared.Examine;
using Content.Shared.Hands;
using Content.Shared.Inventory.Events;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Utility;
namespace Content.Shared._Goobstation.Loudspeaker.Systems;
public sealed class LoudSpeakerSystem : EntitySystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<Components.LoudspeakerComponent, GotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<Components.LoudspeakerComponent, GotUnequippedEvent>(OnUnequipped);
SubscribeLocalEvent<Components.LoudspeakerComponent, GotEquippedHandEvent>(OnEquippedHands);
SubscribeLocalEvent<Components.LoudspeakerComponent, GotUnequippedHandEvent>(OnUnequippedHands);
SubscribeLocalEvent<Components.LoudspeakerHolderComponent, GetLoudspeakerEvent>(GetLoudSpeakers);
SubscribeLocalEvent<Components.LoudspeakerComponent, GetLoudspeakerDataEvent>(OnGetLoudspeakerData);
SubscribeLocalEvent<Components.LoudspeakerHolderComponent, GetSpeechSoundEvent>(OnGetSpeechSound);
SubscribeLocalEvent<Components.LoudspeakerComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<Components.LoudspeakerComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
}
private void OnEquipped(EntityUid uid, Components.LoudspeakerComponent comp, GotEquippedEvent args)
{
if (!args.SlotFlags.HasFlag(comp.RequiredSlot))
return;
EnsureComp<Components.LoudspeakerHolderComponent>(args.Equipee).Loudspeakers.Add(uid);
}
private void OnUnequipped(EntityUid uid, Components.LoudspeakerComponent comp, GotUnequippedEvent args)
{
if (!TryComp<Components.LoudspeakerHolderComponent>(args.Equipee, out var holder))
return;
holder.Loudspeakers.Remove(uid);
DoRemovalCheck(args.Equipee, holder);
}
private void OnEquippedHands(EntityUid uid, Components.LoudspeakerComponent comp, GotEquippedHandEvent args)
{
if (!comp.WorksInHand)
return;
EnsureComp<Components.LoudspeakerHolderComponent>(args.User).Loudspeakers.Add(uid);
}
private void OnUnequippedHands(EntityUid uid, Components.LoudspeakerComponent comp, GotUnequippedHandEvent args)
{
if (!TryComp<Components.LoudspeakerHolderComponent>(args.User, out var holder))
return;
holder.Loudspeakers.Remove(uid);
DoRemovalCheck(args.User, holder);
}
private void GetLoudSpeakers(Entity<Components.LoudspeakerHolderComponent> ent, ref GetLoudspeakerEvent args)
{
args.Loudspeakers = ent.Comp.Loudspeakers;
}
private void OnGetLoudspeakerData(Entity<Components.LoudspeakerComponent> ent, ref GetLoudspeakerDataEvent args)
{
args.IsActive = ent.Comp.IsActive;
args.FontSize = ent.Comp.FontSize;
args.AffectRadio = ent.Comp.AffectRadio;
args.AffectChat = ent.Comp.AffectChat;
args.SpeechSounds = ent.Comp.SpeechSounds;
}
private void OnGetSpeechSound(Entity<Components.LoudspeakerHolderComponent> ent, ref GetSpeechSoundEvent args)
{
foreach (var loudspeaker in ent.Comp.Loudspeakers)
{
var speechEv = new GetLoudspeakerDataEvent();
RaiseLocalEvent(loudspeaker, ref speechEv);
if (speechEv.SpeechSounds != null)
{
args.SpeechSoundProtoId = speechEv.SpeechSounds;
args.Handled = true; // Delta V - bugfix
return;
}
}
}
private void OnExamined(Entity<Components.LoudspeakerComponent> ent, ref ExaminedEvent args)
{
var state = ent.Comp.IsActive ? "on" : "off";
var message = ent.Comp.CanToggle
? Loc.GetString("loudspeaker-examine-toggleable", ("state", state))
: Loc.GetString("loudspeaker-examine-generic");
args.PushMarkup(message);
}
private void OnGetVerbs(Entity<Components.LoudspeakerComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract
|| !ent.Comp.CanToggle)
return;
var user = args.User;
AlternativeVerb toggleLoudspeakerVerb = new()
{
Act = () =>
{
ToggleLoudspeakerEffect(user, ent);
ent.Comp.IsActive = !ent.Comp.IsActive;
Dirty(ent);
},
Text = Loc.GetString("loudspeaker-toggle"),
Icon = new SpriteSpecifier.Rsi(new("/Textures/Effects/text.rsi"), "exclamation"),
};
args.Verbs.Add(toggleLoudspeakerVerb);
}
#region Helper methods
private void DoRemovalCheck(EntityUid equipee, Components.LoudspeakerHolderComponent comp)
{
if (comp.Loudspeakers.Count == 0) // only remove when theres no loudspeakers
{
RemComp<Components.LoudspeakerHolderComponent>(equipee);
return;
}
}
private void ToggleLoudspeakerEffect(EntityUid user, Entity<Components.LoudspeakerComponent> loudspeaker)
{
var state = !loudspeaker.Comp.IsActive ? "on" : "off";
_audio.PlayPredicted(loudspeaker.Comp.ToggleSound, user, user);
_popup.PopupClient(Loc.GetString("loudspeaker-toggle-popup", ("state", state)), user, user);
}
#endregion
}

View File

@ -0,0 +1,4 @@
namespace Content.Shared._Goobstation.Speech;
[ByRefEvent]
public record struct GetSpeechSoundEvent(string? SpeechSoundProtoId = null, bool Handled = false);

View File

@ -0,0 +1,4 @@
- files: ["megaphone.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Taken from TG"
source: "https://github.com/tgstation/tgstation/blob/master/sound/items/megaphone.ogg"

Binary file not shown.

View File

@ -0,0 +1,5 @@
loudspeaker-examine-toggleable = The loudspeaker functionality can be toggled. The loudspeaker is [color=yellow]{$state}[/color].
loudspeaker-examine-generic = This has loudspeaker functionality.
loudspeaker-toggle = Toggle loudspeaker
loudspeaker-toggle-popup = Loudspeaker toggled {$state}.

View File

@ -14,6 +14,7 @@
- id: DoorRemoteCargo
- id: AstroNavCartridge
- id: ClothingHandsKnuckleDustersQM
- id: CargoMegaphone # goob - loudspeaker
- type: entity
id: LockerQuarterMasterFilled
@ -138,6 +139,7 @@
- id: HoPIDCard
#- id: WeaponDisabler # DeltaV - There is no reason for the service head to have a disabler.
- id: ClothingEyesHudCommand
- id: CommandMegaphone # goob - loudspeakers
- type: entity
id: LockerHeadOfPersonnelFilled

View File

@ -57,6 +57,8 @@
#- id: ClothingNeckShockCollar # DeltaV - Moved to dresser I guess?
# amount: 2
# End DeltaV additions
- id: SecurityMegaphone # goob - loudspeakers
- type: entityTable
id: FillLockerWardenHardsuit

View File

@ -11,7 +11,7 @@
equippedPrefix: alt
- type: entity
parent: ClothingHeadsetAlt
parent: ClothingHeadsetAltLoudspeaker
id: ClothingHeadsetAltCargo
name: logistics officer's over-ear headset # DeltaV - Logistics Department replacing Cargo
components:
@ -43,7 +43,7 @@
sprite: Clothing/Ears/Headsets/centcom.rsi
- type: entity
parent: [ClothingHeadsetAlt, BaseCommandContraband]
parent: [ClothingHeadsetAltLoudspeaker, BaseCommandContraband] # goob
id: ClothingHeadsetAltCommand
name: command over-ear headset
components:
@ -57,7 +57,7 @@
sprite: Clothing/Ears/Headsets/command.rsi
- type: entity
parent: [ClothingHeadsetAlt, BaseCommandContraband]
parent: [ClothingHeadsetAltLoudspeaker, BaseCommandContraband] # goob
id: ClothingHeadsetAltEngineering
name: chief engineer's over-ear headset
components:
@ -73,7 +73,7 @@
sprite: Clothing/Ears/Headsets/engineering.rsi
- type: entity
parent: [ClothingHeadsetAlt, BaseCommandContraband]
parent: [ClothingHeadsetAltLoudspeaker, BaseCommandContraband] # goob
id: ClothingHeadsetAltMedical
name: chief medical officer's over-ear headset
components:
@ -91,7 +91,7 @@
stealGroup: ClothingHeadsetAltMedical
- type: entity
parent: [ClothingHeadsetAlt, BaseCommandContraband]
parent: [ClothingHeadsetAltLoudspeaker, BaseCommandContraband] # goob
id: ClothingHeadsetAltSecurity
name: head of security's over-ear headset
components:
@ -107,7 +107,7 @@
sprite: _DV/Clothing/Ears/Headsets/security.rsi # DeltaV - Security uses blue headsets
- type: entity
parent: [ClothingHeadsetAlt, BaseCommandContraband]
parent: [ClothingHeadsetAltLoudspeaker, BaseCommandContraband] # goob
id: ClothingHeadsetAltScience
name: mystagogue's over-ear headset # DeltaV - Epistemics Department replacing Science
components:

View File

@ -608,6 +608,9 @@
stateBaseClosed: clown
stateDoorOpen: clown_open
stateDoorClosed: clown_door
- type: StorageFill
contents:
- id: ClownMegaphone # goob - loudspeakers
# Mime
- type: entity

View File

@ -98,6 +98,7 @@
neck: SyndicateWhistle
pocket2: CommanderUplinkRadio45TC # DeltaV - allows them to buy commander armor
outerClothing: ClothingOuterCoatSyndieCapArmored # DeltaV - removal of armor
ears: ClothingHeadsetAltSyndicateCommander # goob - loudspeaker
inhand:
- NukeOpsDeclarationOfWar

View File

@ -1,5 +1,5 @@
- type: entity
parent: ClothingHeadsetAlt
parent: ClothingHeadsetAltLoudspeaker
id: ClothingHeadsetAltService
name: head of personnel's over-ear headset
components:
@ -15,7 +15,7 @@
sprite: _DV/Clothing/Ears/Headsets/service.rsi
- type: entity
parent: ClothingHeadset
parent: ClothingHeadsetAltLoudspeaker
id: ClothingHeadsetAltJustice
name: chief justice's headset
description: The headset used by the chief justice.
@ -34,7 +34,7 @@
sprite: _DV/Clothing/Ears/Headsets/justice.rsi
- type: entity
parent: ClothingHeadsetAltSyndicate
parent: [ ClothingHeadsetAltSyndicate, ClothingHeadsetAltLoudspeaker ]
id: ClothingHeadsetAltSyndicateListening
name: blood-red over-ear interception headset
description: An advanced, modular syndicate interception intercom that fits over the head.

View File

@ -0,0 +1,13 @@
- type: entity
abstract: true
parent: ClothingHeadsetAlt
id: ClothingHeadsetAltLoudspeaker
components:
- type: Loudspeaker
canToggle: true
affectRadio: true
- type: entity
parent: [ClothingHeadsetAltSyndicate, ClothingHeadsetAltLoudspeaker]
id: ClothingHeadsetAltSyndicateCommander
name: blood-red commander over-ear headset

View File

@ -0,0 +1,66 @@
- type: entity
parent: BaseItem
id: Megaphone
name: megaphone
description: Testing!
components:
- type: Loudspeaker
isActive: true
worksInHand: true
affectChat: true
speechSounds: Loudspeaker
- type: Sprite
sprite: _Goobstation/Objects/Tools/megaphones/generic.rsi
state: icon
- type: Item
sprite: _Goobstation/Objects/Tools/megaphones/generic.rsi
- type: entity
parent: Megaphone
id: CargoMegaphone
name: cargo megaphone
description: Make money in silence and let the hustle make noise. Or something like that.
components:
- type: Sprite
sprite: _Goobstation/Objects/Tools/megaphones/cargo.rsi
state: icon
- type: Item
sprite: _Goobstation/Objects/Tools/megaphones/cargo.rsi
- type: entity
parent: Megaphone
id: CommandMegaphone
name: command megaphone
description: Turn shallow authority into loud shallow authority.
components:
- type: Sprite
sprite: _Goobstation/Objects/Tools/megaphones/command.rsi
state: icon
- type: Item
sprite: _Goobstation/Objects/Tools/megaphones/command.rsi
- type: entity
parent: Megaphone
id: SecurityMegaphone
name: security megaphone
description: Use to make suspects aware you're after them. Or to yell at prisoners you don't like.
components:
- type: Sprite
sprite: _Goobstation/Objects/Tools/megaphones/security.rsi
state: icon
- type: Item
sprite: _Goobstation/Objects/Tools/megaphones/security.rsi
- type: entity
parent: Megaphone
id: ClownMegaphone
name: clown megaphone
description: A comedian has to make their voice known.
components:
- type: Loudspeaker
affectRadio: true # :trollface:
- type: Sprite
sprite: _Goobstation/Objects/Tools/megaphones/clown.rsi
state: icon
- type: Item
sprite: _Goobstation/Objects/Tools/megaphones/clown.rsi

View File

@ -0,0 +1,8 @@
- type: speechSounds
id: Loudspeaker
saySound:
path: /Audio/_Goobstation/Loudspeaker/megaphone.ogg
askSound:
path: /Audio/_Goobstation/Loudspeaker/megaphone.ogg
exclaimSound:
path: /Audio/_Goobstation/Loudspeaker/megaphone.ogg

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

View File

@ -0,0 +1,22 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_lefthand.dmi, https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_righthand.dmi and https://github.com/tgstation/tgstation/blob/master/icons/obj/devices/voice.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions" : 4
},
{
"name": "inhand-right",
"directions" : 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

View File

@ -0,0 +1,22 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_lefthand.dmi, https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_righthand.dmi and https://github.com/tgstation/tgstation/blob/master/icons/obj/devices/voice.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions" : 4
},
{
"name": "inhand-right",
"directions" : 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View File

@ -0,0 +1,22 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_lefthand.dmi, https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_righthand.dmi and https://github.com/tgstation/tgstation/blob/master/icons/obj/devices/voice.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions" : 4
},
{
"name": "inhand-right",
"directions" : 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

View File

@ -0,0 +1,22 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_lefthand.dmi, https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_righthand.dmi and https://github.com/tgstation/tgstation/blob/master/icons/obj/devices/voice.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions" : 4
},
{
"name": "inhand-right",
"directions" : 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

View File

@ -0,0 +1,22 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_lefthand.dmi, https://github.com/tgstation/tgstation/blob/master/icons/mob/inhands/items/megaphone_righthand.dmi and https://github.com/tgstation/tgstation/blob/master/icons/obj/devices/voice.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions" : 4
},
{
"name": "inhand-right",
"directions" : 4
}
]
}