mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
Portable music player, remote jukebox speaker
This commit is contained in:
@@ -325,10 +325,13 @@ var/list/runechat_image_cache = list()
|
||||
if(5)
|
||||
return rgb(c,m,x)
|
||||
|
||||
/atom/proc/runechat_message(message, range = world.view, italics, list/classes = list(), audible = TRUE)
|
||||
var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src), range, remote_ghosts = FALSE)
|
||||
|
||||
var/list/hearing_mobs = hear["mobs"]
|
||||
/atom/proc/runechat_message(message, range = world.view, italics, list/classes = list(), audible = TRUE, list/specific_viewers)
|
||||
var/hearing_mobs
|
||||
if(islist(specific_viewers))
|
||||
hearing_mobs = specific_viewers.Copy()
|
||||
else
|
||||
var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src), range, remote_ghosts = FALSE)
|
||||
hearing_mobs = hear["mobs"]
|
||||
|
||||
for(var/mob in hearing_mobs)
|
||||
var/mob/M = mob
|
||||
|
||||
@@ -110,4 +110,21 @@
|
||||
access = list(access_explorer,
|
||||
access_eva,
|
||||
access_pilot)
|
||||
one_access = TRUE
|
||||
one_access = TRUE
|
||||
|
||||
/datum/supply_pack/misc/music_players
|
||||
name = "music players (3)"
|
||||
contains = list(
|
||||
/obj/item/device/walkpod = 3
|
||||
)
|
||||
cost = 150
|
||||
containername = "portable music players crate"
|
||||
|
||||
/datum/supply_pack/misc/juke_remotes
|
||||
name = "jukebox remote speakers (2)"
|
||||
contains = list(
|
||||
/obj/item/device/juke_remote = 2
|
||||
)
|
||||
cost = 300
|
||||
containername = "cordless jukebox speakers crate"
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
var/freq = 0 // Currently no effect, will return in phase II of mediamanager.
|
||||
//VOREStation Add
|
||||
var/loop_mode = JUKEMODE_PLAY_ONCE // Behavior when finished playing a song
|
||||
var/max_queue_len = 3 // How many songs are we allowed to queue up?
|
||||
var/list/queue = list()
|
||||
var/list/obj/item/device/juke_remote/remotes
|
||||
//VOREStation Add End
|
||||
var/datum/track/current_track
|
||||
|
||||
@@ -60,29 +59,24 @@
|
||||
// If the current track isn't finished playing, let it keep going
|
||||
if(current_track && world.time < media_start_time + current_track.duration)
|
||||
return
|
||||
// Otherwise time to pick a new one!
|
||||
if(queue.len > 0)
|
||||
current_track = queue[1]
|
||||
queue.Cut(1, 2) // Remove the item we just took off the list
|
||||
else
|
||||
// Oh... nothing in queue? Well then pick next according to our rules
|
||||
var/list/tracks = getTracksList()
|
||||
switch(loop_mode)
|
||||
if(JUKEMODE_NEXT)
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
|
||||
current_track = tracks[newTrackIndex]
|
||||
if(JUKEMODE_RANDOM)
|
||||
var/previous_track = current_track
|
||||
do
|
||||
current_track = pick(tracks)
|
||||
while(current_track == previous_track && tracks.len > 1)
|
||||
if(JUKEMODE_REPEAT_SONG)
|
||||
current_track = current_track
|
||||
if(JUKEMODE_PLAY_ONCE)
|
||||
current_track = null
|
||||
playing = 0
|
||||
update_icon()
|
||||
// Oh... nothing in queue? Well then pick next according to our rules
|
||||
var/list/tracks = getTracksList()
|
||||
switch(loop_mode)
|
||||
if(JUKEMODE_NEXT)
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
|
||||
current_track = tracks[newTrackIndex]
|
||||
if(JUKEMODE_RANDOM)
|
||||
var/previous_track = current_track
|
||||
do
|
||||
current_track = pick(tracks)
|
||||
while(current_track == previous_track && tracks.len > 1)
|
||||
if(JUKEMODE_REPEAT_SONG)
|
||||
current_track = current_track
|
||||
if(JUKEMODE_PLAY_ONCE)
|
||||
current_track = null
|
||||
playing = 0
|
||||
update_icon()
|
||||
updateDialog()
|
||||
start_stop_song()
|
||||
|
||||
@@ -96,6 +90,11 @@
|
||||
media_url = ""
|
||||
media_start_time = 0
|
||||
update_music()
|
||||
//VOREStation Add
|
||||
for(var/rem in remotes)
|
||||
var/obj/item/device/juke_remote/remote = rem
|
||||
remote.update_music()
|
||||
//VOREStation Add End
|
||||
|
||||
/obj/machinery/media/jukebox/proc/set_hacked(var/newhacked)
|
||||
if(hacked == newhacked)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
111
code/modules/media/juke_remote.dm
Normal file
111
code/modules/media/juke_remote.dm
Normal file
@@ -0,0 +1,111 @@
|
||||
/obj/item/device/juke_remote
|
||||
name = "\improper BoomTown cordless speaker"
|
||||
desc = "Once paired with a jukebox, this speaker can relay the tunes elsewhere!"
|
||||
|
||||
description_fluff = "The BoomTown cordless speaker is capable of maintaining a high-quality 49kbps audio stream from a stationary jukebox and relaying the sound locally. It's like magic!"
|
||||
description_info = "Hit it on a jukebox to pair, then set it down to play tunes. Does nothing while held, it has to be stationary, visible in the world. Keep in mind music is done by AREA, not within a certain range. You will need more than one to cover a department."
|
||||
|
||||
icon = 'icons/obj/device_vr.dmi'
|
||||
icon_state = "bspeaker"
|
||||
|
||||
var/obj/machinery/media/jukebox/paired_juke
|
||||
var/area/our_area
|
||||
|
||||
/*
|
||||
/obj/item/device/juke_remote/Initialize()
|
||||
. = ..()
|
||||
flags |= NOBLUDGEON
|
||||
*/
|
||||
// Pairing
|
||||
/obj/item/device/juke_remote/proc/pair_juke(obj/machinery/media/jukebox/juke, mob/user)
|
||||
if(paired_juke)
|
||||
to_chat(user, "<span class='warning'>The [src] is already paired to [paired_juke == juke ? "that" : "a different"] jukebox.</span>")
|
||||
return
|
||||
paired_juke = juke
|
||||
LAZYDISTINCTADD(paired_juke.remotes, src)
|
||||
to_chat(user, "<span class='notice'>You pair the [src] to the [juke].</span>")
|
||||
icon_state = "[initial(icon_state)]_ready"
|
||||
|
||||
/obj/item/device/juke_remote/proc/unpair_juke(mob/user)
|
||||
if(!paired_juke)
|
||||
to_chat(user, "<span class='warning'>The [src] isn't paired to anything.</span>")
|
||||
return
|
||||
LAZYREMOVE(paired_juke.remotes, src)
|
||||
paired_juke = null
|
||||
icon_state = initial(icon_state)
|
||||
unanchor()
|
||||
detach_area()
|
||||
to_chat(user, "<span class='notice'>You unpair the [src].</span>")
|
||||
icon_state = "[initial(icon_state)]"
|
||||
|
||||
/obj/item/device/juke_remote/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(istype(target, /obj/machinery/media/jukebox))
|
||||
pair_juke(target, user)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/device/juke_remote/verb/reset()
|
||||
set name = "Reset Pairing"
|
||||
set desc = "Unpair this speaker from a jukebox."
|
||||
|
||||
unpair_juke(usr)
|
||||
|
||||
// Deploying
|
||||
/obj/item/device/juke_remote/Moved(atom/old_loc, direction, forced)
|
||||
. = ..()
|
||||
if(paired_juke && !anchored && isturf(loc))
|
||||
anchor()
|
||||
|
||||
/obj/item/device/juke_remote/attack_hand(mob/living/user)
|
||||
if(anchored)
|
||||
unanchor()
|
||||
return ..()
|
||||
|
||||
/obj/item/device/juke_remote/proc/anchor()
|
||||
if(anchored)
|
||||
return
|
||||
anchored = TRUE
|
||||
if(attach_area())
|
||||
visible_message("[src] attaches to the nearest surface and bounces happily, ready to pump tunes.", runemessage = "clank")
|
||||
if(paired_juke) // we were able to claim the area
|
||||
icon_state = "[initial(icon_state)]_playing"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]"
|
||||
|
||||
/obj/item/device/juke_remote/proc/unanchor()
|
||||
detach_area()
|
||||
anchored = FALSE
|
||||
visible_message("[src] detaches from it's mounting surface, able to be moved once again.", runemessage = "clunk")
|
||||
if(paired_juke)
|
||||
icon_state = "[initial(icon_state)]_ready"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]"
|
||||
|
||||
// Area handling
|
||||
/obj/item/device/juke_remote/proc/attach_area()
|
||||
var/area/A = get_area(src)
|
||||
if(!A || !paired_juke)
|
||||
error("Jukebox remote at [x],[y],[z] without paired juke tried to bind to an area.")
|
||||
return FALSE
|
||||
if(A.media_source)
|
||||
return FALSE // Already has a media source, won't overpower it with porta speaker
|
||||
our_area = A
|
||||
A.media_source = paired_juke
|
||||
update_music()
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/juke_remote/proc/detach_area()
|
||||
if(!our_area || (paired_juke && our_area.media_source != paired_juke))
|
||||
return
|
||||
our_area.media_source = null
|
||||
update_music()
|
||||
our_area = null
|
||||
|
||||
// Music handling
|
||||
/obj/item/device/juke_remote/proc/update_music()
|
||||
if(!our_area || !paired_juke)
|
||||
return
|
||||
// Send update to clients.
|
||||
for(var/mob/M in mobs_in_area(our_area))
|
||||
if(M?.client)
|
||||
M.update_music()
|
||||
268
code/modules/media/walkpod.dm
Normal file
268
code/modules/media/walkpod.dm
Normal file
@@ -0,0 +1,268 @@
|
||||
// Mostly a jukebox copy-paste, given the vastly different paths though it seemed worth it.
|
||||
// Would rather not have a bunch of /machinery baggage on our portable music player.
|
||||
|
||||
/obj/item/device/walkpod
|
||||
name = "\improper PodZu music player"
|
||||
desc = "Portable music player! For when you need to ignore the rest of the world, there's only one choice: PodZu."
|
||||
description_fluff = "A prestigious set: The ZuMan music player, and the HeadPods headphones, both 90th anniversary releases! Together they form the PodZu Music Player, famous in the local galactic cluster for pumping sick beats directly into your head."
|
||||
description_info = "An easy way to access the menu while the player is in a pocket is Alt-Click. Wearing the headphones is not actually necessary to listen to music, but you can if you want, by right-clicking on the player and using 'Take HeadPods'."
|
||||
|
||||
icon = 'icons/obj/device_vr.dmi'
|
||||
icon_state = "podzu" // podzu_o, headpod, zuman
|
||||
|
||||
var/loop_mode = JUKEMODE_PLAY_ONCE // Behavior when finished playing a song
|
||||
var/datum/track/current_track // Current track playing
|
||||
var/mob/living/listener // Person whomst is listening to us
|
||||
|
||||
var/playing = 0
|
||||
var/volume = 1
|
||||
|
||||
var/media_url = ""
|
||||
var/media_start_time
|
||||
|
||||
var/obj/item/device/headpods/deployed_headpods
|
||||
|
||||
w_class = ITEMSIZE_COST_SMALL
|
||||
|
||||
/obj/item/device/walkpod/Destroy()
|
||||
remove_listener()
|
||||
return ..()
|
||||
|
||||
// Icon
|
||||
/obj/item/device/walkpod/update_icon()
|
||||
if(listener)
|
||||
if(deployed_headpods)
|
||||
icon_state = "zuman_on"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]_on"
|
||||
else
|
||||
if(deployed_headpods)
|
||||
icon_state = "zuman"
|
||||
else
|
||||
icon_state = "[initial(icon_state)]"
|
||||
|
||||
// Listener handling
|
||||
/obj/item/device/walkpod/proc/check_listener()
|
||||
if(loc == listener)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/device/walkpod/proc/remove_listener()
|
||||
if(playing)
|
||||
StopPlaying()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
if(deployed_headpods)
|
||||
restore_headpods()
|
||||
to_chat(listener, "<span class='notice'>You are no longer wearing the [src]'s headphones.</span>")
|
||||
listener = null
|
||||
update_icon()
|
||||
|
||||
/obj/item/device/walkpod/proc/set_listener(mob/living/L)
|
||||
if(listener)
|
||||
remove_listener()
|
||||
listener = L
|
||||
START_PROCESSING(SSobj, src)
|
||||
to_chat(L, "<span class='notice'>You put the [src]'s headphones on and power it up, preparing to listen to some <b>sick tunes</b>.</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/item/device/walkpod/proc/update_music()
|
||||
listener?.force_music(media_url, media_start_time, volume) // Calling this with "" url (when we aren't playing) helpfully disables forced music
|
||||
|
||||
/obj/item/device/walkpod/AltClick(mob/living/L)
|
||||
if(L == listener && check_listener())
|
||||
tgui_interact(L)
|
||||
else if(loc == L) // at least they're holding it
|
||||
to_chat(L, "<span class='warning'>Turn on the [src] first.</span>")
|
||||
|
||||
/obj/item/device/walkpod/attack_self(mob/living/L)
|
||||
if(!istype(L) || loc != L)
|
||||
return
|
||||
if(!listener)
|
||||
set_listener(L)
|
||||
tgui_interact(L)
|
||||
|
||||
// Process ticks to ensure our listener remains valid and we do music-ing
|
||||
/obj/item/device/walkpod/process()
|
||||
if(!check_headpods())
|
||||
restore_headpods()
|
||||
if(!check_listener())
|
||||
remove_listener()
|
||||
return
|
||||
if(!playing)
|
||||
return
|
||||
// If the current track isn't finished playing, let it keep going
|
||||
if(current_track && world.time < media_start_time + current_track.duration)
|
||||
return
|
||||
// Oh... nothing in queue? Well then pick next according to our rules
|
||||
var/list/tracks = getTracksList()
|
||||
switch(loop_mode)
|
||||
if(JUKEMODE_NEXT)
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
|
||||
current_track = tracks[newTrackIndex]
|
||||
if(JUKEMODE_RANDOM)
|
||||
var/previous_track = current_track
|
||||
do
|
||||
current_track = pick(tracks)
|
||||
while(current_track == previous_track && tracks.len > 1)
|
||||
if(JUKEMODE_REPEAT_SONG)
|
||||
current_track = current_track
|
||||
if(JUKEMODE_PLAY_ONCE)
|
||||
current_track = null
|
||||
playing = 0
|
||||
update_icon()
|
||||
updateDialog()
|
||||
start_stop_song()
|
||||
|
||||
// Track/music internals
|
||||
/obj/item/device/walkpod/proc/start_stop_song()
|
||||
if(current_track && playing)
|
||||
media_url = current_track.url
|
||||
media_start_time = world.time
|
||||
runechat_message("* [current_track.display()] *", specific_viewers = list(listener))
|
||||
else
|
||||
media_url = ""
|
||||
media_start_time = 0
|
||||
update_music()
|
||||
|
||||
/obj/item/device/walkpod/proc/StopPlaying()
|
||||
playing = 0
|
||||
start_stop_song()
|
||||
|
||||
/obj/item/device/walkpod/proc/StartPlaying()
|
||||
if(!current_track)
|
||||
return
|
||||
playing = 1
|
||||
start_stop_song()
|
||||
updateDialog()
|
||||
|
||||
// Advance to the next track - Don't start playing it unless we were already playing
|
||||
/obj/item/device/walkpod/proc/NextTrack()
|
||||
var/list/tracks = getTracksList()
|
||||
if(!tracks.len) return
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
var/newTrackIndex = (curTrackIndex % tracks.len) + 1 // Loop back around if past end
|
||||
current_track = tracks[newTrackIndex]
|
||||
if(playing)
|
||||
start_stop_song()
|
||||
updateDialog()
|
||||
|
||||
// Unadvance to the notnext track - Don't start playing it unless we were already playing
|
||||
/obj/item/device/walkpod/proc/PrevTrack()
|
||||
var/list/tracks = getTracksList()
|
||||
if(!tracks.len) return
|
||||
var/curTrackIndex = max(1, tracks.Find(current_track))
|
||||
var/newTrackIndex = curTrackIndex == 1 ? tracks.len : curTrackIndex - 1
|
||||
current_track = tracks[newTrackIndex]
|
||||
if(playing)
|
||||
start_stop_song()
|
||||
updateDialog()
|
||||
|
||||
// UI
|
||||
/obj/item/device/walkpod/proc/getTracksList()
|
||||
return SSmedia_tracks.jukebox_tracks
|
||||
|
||||
/obj/item/device/walkpod/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Jukebox", "PodZu Music Player")
|
||||
ui.open()
|
||||
|
||||
/obj/item/device/walkpod/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/list/data = ..()
|
||||
|
||||
data["playing"] = playing
|
||||
data["loop_mode"] = loop_mode
|
||||
data["volume"] = volume
|
||||
data["current_track_ref"] = null
|
||||
data["current_track"] = null
|
||||
data["current_genre"] = null
|
||||
if(current_track)
|
||||
data["current_track_ref"] = "\ref[current_track]" // Convenient shortcut
|
||||
data["current_track"] = current_track.toTguiList()
|
||||
data["current_genre"] = current_track.genre
|
||||
data["percent"] = playing ? min(100, round(world.time - media_start_time) / current_track.duration) : 0;
|
||||
|
||||
var/list/tgui_tracks = list()
|
||||
for(var/datum/track/T in getTracksList())
|
||||
tgui_tracks.Add(list(T.toTguiList()))
|
||||
data["tracks"] = tgui_tracks
|
||||
|
||||
return data
|
||||
|
||||
/obj/item/device/walkpod/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
switch(action)
|
||||
if("change_track")
|
||||
var/datum/track/T = locate(params["change_track"]) in getTracksList()
|
||||
if(istype(T))
|
||||
current_track = T
|
||||
StartPlaying()
|
||||
return TRUE
|
||||
if("loopmode")
|
||||
var/newval = text2num(params["loopmode"])
|
||||
loop_mode = sanitize_inlist(newval, list(JUKEMODE_NEXT, JUKEMODE_RANDOM, JUKEMODE_REPEAT_SONG, JUKEMODE_PLAY_ONCE), loop_mode)
|
||||
return TRUE
|
||||
if("volume")
|
||||
var/newval = text2num(params["val"])
|
||||
volume = clamp(newval, 0, 1)
|
||||
update_music() // To broadcast volume change without restarting song
|
||||
return TRUE
|
||||
if("stop")
|
||||
StopPlaying()
|
||||
return TRUE
|
||||
if("play")
|
||||
if(current_track == null)
|
||||
to_chat(usr, "No track selected.")
|
||||
else
|
||||
StartPlaying()
|
||||
return TRUE
|
||||
|
||||
// Silly verb
|
||||
/obj/item/device/walkpod/verb/take_headpods()
|
||||
set name = "Take HeadPods"
|
||||
set desc = "Grab the pair of HeadPods."
|
||||
|
||||
var/mob/living/L = usr
|
||||
if(!istype(L))
|
||||
return
|
||||
if(deployed_headpods)
|
||||
to_chat(usr, "<span class='warning'>The HeadPods are already deployed!</span>")
|
||||
return
|
||||
deployed_headpods = new ()
|
||||
L.put_in_any_hand_if_possible(deployed_headpods)
|
||||
update_icon()
|
||||
|
||||
/obj/item/device/walkpod/attackby(obj/item/W, mob/user)
|
||||
if(W == deployed_headpods)
|
||||
restore_headpods(user)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/device/walkpod/proc/restore_headpods(mob/living/potential_holder)
|
||||
if(!deployed_headpods)
|
||||
return
|
||||
|
||||
if(listener)
|
||||
to_chat(listener, "<span class='notice'>The headphone cable reunites the [deployed_headpods] with the [src] by retracting inwards.</span>")
|
||||
|
||||
if(istype(potential_holder))
|
||||
potential_holder.unEquip(deployed_headpods, force = TRUE)
|
||||
qdel_null(deployed_headpods)
|
||||
update_icon()
|
||||
|
||||
/obj/item/device/walkpod/proc/check_headpods()
|
||||
if(deployed_headpods && deployed_headpods.loc != loc)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/headpods
|
||||
name = "\improper pair of HeadPods"
|
||||
desc = "Portable listening in Hi-Fi!"
|
||||
icon = 'icons/obj/device_vr.dmi'
|
||||
icon_state = "headpods"
|
||||
item_state = "headphones_on"
|
||||
w_class = ITEMSIZE_SMALL
|
||||
slot_flags = SLOT_HEAD
|
||||
@@ -53,3 +53,18 @@
|
||||
build_path = /obj/item/weapon/mining_scanner/advanced
|
||||
sort_string = "FBAAB"
|
||||
|
||||
/datum/design/item/general/walkpod
|
||||
name = "PodZu Music Player"
|
||||
id = "walkpod"
|
||||
req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 3)
|
||||
materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_GLASS = 2000)
|
||||
build_path = /obj/item/device/walkpod
|
||||
sort_string = "TCVAD"
|
||||
|
||||
/datum/design/item/general/juke_remote
|
||||
name = "BoomTown Cordless Speaker"
|
||||
id = "juke_remote"
|
||||
req_tech = list(TECH_MAGNET = 3, TECH_ENGINEERING = 4, TECH_BLUESPACE = 1)
|
||||
materials = list(DEFAULT_WALL_MATERIAL = 4000, MAT_GLASS = 4000, MAT_URANIUM = 2000)
|
||||
build_path = /obj/item/device/juke_remote
|
||||
sort_string = "TCVAE"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 7.5 KiB |
@@ -2685,12 +2685,14 @@
|
||||
#include "code\modules\materials\sheets\organic\tanning\hide_hairless.dm"
|
||||
#include "code\modules\materials\sheets\organic\tanning\leather_wet.dm"
|
||||
#include "code\modules\materials\sheets\organic\tanning\tanning_rack.dm"
|
||||
#include "code\modules\media\juke_remote.dm"
|
||||
#include "code\modules\media\media_machinery.dm"
|
||||
#include "code\modules\media\media_player_html5.dm"
|
||||
#include "code\modules\media\media_player_vlc.dm"
|
||||
#include "code\modules\media\media_player_wmp.dm"
|
||||
#include "code\modules\media\media_tracks.dm"
|
||||
#include "code\modules\media\mediamanager.dm"
|
||||
#include "code\modules\media\walkpod.dm"
|
||||
#include "code\modules\metric\activity.dm"
|
||||
#include "code\modules\metric\count.dm"
|
||||
#include "code\modules\metric\department.dm"
|
||||
|
||||
Reference in New Issue
Block a user