Ports /tg/ spatial grid system, refactors telecomms, destroys lag (#15140)

This commit is contained in:
Wildkins
2022-12-09 06:35:33 -05:00
committed by GitHub
parent d3f8f12fea
commit 2fcfa8adb7
158 changed files with 4068 additions and 3263 deletions

View File

@@ -40,6 +40,7 @@
#include "code\__defines\ghostspawner.dm"
#include "code\__defines\guns.dm"
#include "code\__defines\hydroponics.dm"
#include "code\__defines\important_recursive_contents.dm"
#include "code\__defines\items_clothing.dm"
#include "code\__defines\jobs.dm"
#include "code\__defines\lighting.dm"
@@ -68,6 +69,7 @@
#include "code\__defines\shuttle.dm"
#include "code\__defines\space_sectors.dm"
#include "code\__defines\spaceman_dmm.dm"
#include "code\__defines\spatial_gridmap.dm"
#include "code\__defines\species.dm"
#include "code\__defines\species_languages.dm"
#include "code\__defines\subsystem-defines.dm"
@@ -75,6 +77,7 @@
#include "code\__defines\targeting.dm"
#include "code\__defines\tgs.dm"
#include "code\__defines\time.dm"
#include "code\__defines\traits.dm"
#include "code\__defines\turfs.dm"
#include "code\__defines\vueui.dm"
#include "code\__defines\webhook.dm"
@@ -106,6 +109,7 @@
#include "code\_helpers\names.dm"
#include "code\_helpers\overlay.dm"
#include "code\_helpers\sanitize_values.dm"
#include "code\_helpers\spatial_info.dm"
#include "code\_helpers\spawn_sync.dm"
#include "code\_helpers\text.dm"
#include "code\_helpers\time.dm"
@@ -207,6 +211,7 @@
#include "code\controllers\subsystems\records.dm"
#include "code\controllers\subsystems\responseteam.dm"
#include "code\controllers\subsystems\skybox.dm"
#include "code\controllers\subsystems\spatial_gridmap.dm"
#include "code\controllers\subsystems\statistics.dm"
#include "code\controllers\subsystems\stickyban.dm"
#include "code\controllers\subsystems\sun.dm"
@@ -765,7 +770,6 @@
#include "code\game\machinery\computer\computer.dm"
#include "code\game\machinery\computer\guestpass.dm"
#include "code\game\machinery\computer\law.dm"
#include "code\game\machinery\computer\message.dm"
#include "code\game\machinery\computer\Operating.dm"
#include "code\game\machinery\computer\pod.dm"
#include "code\game\machinery\computer\RCON_Console.dm"
@@ -798,13 +802,22 @@
#include "code\game\machinery\embedded_controller\simple_docking_controller.dm"
#include "code\game\machinery\pipe\construction.dm"
#include "code\game\machinery\pipe\pipe_dispenser.dm"
#include "code\game\machinery\telecomms\broadcaster.dm"
#include "code\game\machinery\telecomms\logbrowser.dm"
#include "code\game\machinery\telecomms\broadcasting.dm"
#include "code\game\machinery\telecomms\machine_interactions.dm"
#include "code\game\machinery\telecomms\presets.dm"
#include "code\game\machinery\telecomms\telecomunications.dm"
#include "code\game\machinery\telecomms\telemonitor.dm"
#include "code\game\machinery\telecomms\traffic_control.dm"
#include "code\game\machinery\telecomms\telecommunications.dm"
#include "code\game\machinery\telecomms\computers\logbrowser.dm"
#include "code\game\machinery\telecomms\computers\message.dm"
#include "code\game\machinery\telecomms\computers\telemonitor.dm"
#include "code\game\machinery\telecomms\computers\traffic_control.dm"
#include "code\game\machinery\telecomms\machines\allinone.dm"
#include "code\game\machinery\telecomms\machines\broadcaster.dm"
#include "code\game\machinery\telecomms\machines\bus.dm"
#include "code\game\machinery\telecomms\machines\hub.dm"
#include "code\game\machinery\telecomms\machines\message_server.dm"
#include "code\game\machinery\telecomms\machines\processor.dm"
#include "code\game\machinery\telecomms\machines\receiver.dm"
#include "code\game\machinery\telecomms\machines\server.dm"
#include "code\game\modifiers\modifiers.dm"
#include "code\game\objects\buckling.dm"
#include "code\game\objects\empulse.dm"
@@ -2876,7 +2889,6 @@
#include "code\modules\recycling\sortingmachinery.dm"
#include "code\modules\research\circuitprinter.dm"
#include "code\modules\research\destructive_analyzer.dm"
#include "code\modules\research\message_server.dm"
#include "code\modules\research\protolathe.dm"
#include "code\modules\research\rd-readme.dm"
#include "code\modules\research\rdconsole.dm"

View File

@@ -6,6 +6,10 @@
#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
/// Signifies that this proc is used to handle signals.
/// Every proc you pass to RegisterSignal must have this.
#define SIGNAL_HANDLER SHOULD_NOT_SLEEP(TRUE)
/// A wrapper for _AddElement that allows us to pretend we're using normal named arguments
#define AddElement(arguments...) _AddElement(list(##arguments))

View File

@@ -5,6 +5,11 @@
// global signals
// These are signals which can be listened to by any component on any parent
// when we expand maxz
#define COMSIG_GLOB_NEW_Z "!new_z"
// when we expand maxx and maxy (do we ever do this)
#define COMSIG_GLOB_EXPANDED_WORLD_BOUNDS "!expanded_world_bounds"
//////////////////////////////////////////////////////////////////
// /datum signals
@@ -29,6 +34,20 @@
// /turf signals
// /atom/movable signals
#define COMSIG_MOVABLE_MOVED "movable_moved"
#define COMSIG_MOVABLE_HEAR "movable_hear"
#define HEARING_MESSAGE 1
#define HEARING_SPEAKER 2
#define HEARING_LANGUAGE 3
#define HEARING_RAW_MESSAGE 4
//spatial grid signals
///Called from base of /datum/controller/subsystem/spatial_grid/proc/enter_cell: (/atom/movable)
#define SPATIAL_GRID_CELL_ENTERED(contents_type) "spatial_grid_cell_entered_[contents_type]"
///Called from base of /datum/controller/subsystem/spatial_grid/proc/exit_cell: (/atom/movable)
#define SPATIAL_GRID_CELL_EXITED(contents_type) "spatial_grid_cell_exited_[contents_type]"
// /mob signals

View File

@@ -0,0 +1,7 @@
// the area channel of the important_recursive_contents list, everything in here will be sent a signal when their last holding object changes areas
#define RECURSIVE_CONTENTS_AREA_SENSITIVE "recursive_contents_area_sensitive"
// the hearing channel of the important_recursive_contents list, everything in here will count as a hearing atom
#define RECURSIVE_CONTENTS_HEARING_SENSITIVE "recursive_contents_hearing_sensitive"
// the client mobs channel of the important_recursive_contents list, everything in here will be a mob with an attached client
// this is given to both a clients mob, and a clients eye, both point to the clients mob
#define RECURSIVE_CONTENTS_CLIENT_MOBS "recursive_contents_client_mobs"

View File

@@ -10,6 +10,15 @@
#define LAZYPICK(L,DEFAULT) (LAZYLEN(L) ? pick(L) : DEFAULT)
#define LAZYISIN(L, I) (L ? (I in L) : FALSE)
#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I;
#define LAZYADDASSOCLIST(L, K, V) if(!L) { L = list(); } L[K] += list(V);
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }
#define LAZYACCESSASSOC(L, I, K) L ? L[I] ? L[I][K] ? L[I][K] : null : null : null
/// Performs an insertion on the given lazy list with the given key and value. If the value already exists, a new one will not be made.
#define LAZYORASSOCLIST(lazy_list, key, value) \
LAZYINITLIST(lazy_list); \
LAZYINITLIST(lazy_list[key]); \
lazy_list[key] |= value;
// Shims for some list procs in lists.dm.
#define isemptylist(L) (!LAZYLEN(L))

View File

@@ -21,6 +21,7 @@
#define TCMB 2.7 // -270.3 degrees celcius
#define QUANTIZE(variable) (round(variable,0.0001))
#define ROUND_UP(x) ( -round(-(x)))
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
#define INFINITY 1.#INF

View File

@@ -2,36 +2,42 @@
#define PUBLIC_LOW_FREQ 1441
#define PUBLIC_HIGH_FREQ 1489
#define RADIO_HIGH_FREQ 1600
// Reminder: frequencies should only be odd numbers
// Public frequencies (no encryption key required), see range above
#define BOT_FREQ 1447
#define COMM_FREQ 1353
#define ERT_FREQ 1345
#define AI_FREQ 1343
#define DTH_FREQ 1341
#define PEN_FREQ 1451
#define PUB_FREQ 1459
#define ENT_FREQ 1461
#define HAIL_FREQ 1463
#define SEC_I_FREQ 1475
#define MED_I_FREQ 1485
// Department / private frequencies
#define SYND_FREQ 1213
#define BLSP_FREQ 1253
#define NINJ_FREQ 1255
#define BURG_FREQ 1257
#define RAID_FREQ 1277
#define SHIP_FREQ 1280
#define ENT_FREQ 1461 //entertainment frequency. This is not a diona exclusive frequency.
#define DTH_FREQ 1341
#define AI_FREQ 1343
#define ERT_FREQ 1345
#define COMM_FREQ 1353
// department channels
var/const/PUB_FREQ = 1459
var/const/PEN_FREQ = 1451
var/const/SEC_FREQ = 1359
var/const/ENG_FREQ = 1357
var/const/MED_FREQ = 1355
var/const/SCI_FREQ = 1351
var/const/SRV_FREQ = 1349
var/const/SUP_FREQ = 1347
#define SUP_FREQ 1347
#define SRV_FREQ 1349
#define SCI_FREQ 1351
#define MED_FREQ 1355
#define ENG_FREQ 1357
#define SEC_FREQ 1359
// internal department channels
#define MED_I_FREQ 1485
#define SEC_I_FREQ 1475
var/list/AWAY_FREQS_UNASSIGNED = list(1491, 1493, 1495, 1497, 1499, 1501, 1503, 1505, 1507, 1509)
var/list/AWAY_FREQS_ASSIGNED = list("Hailing" = HAIL_FREQ)
var/list/radiochannels = list(
"Common" = PUB_FREQ,
"Hailing" = HAIL_FREQ,
"Science" = SCI_FREQ,
"Command" = COMM_FREQ,
"Medical" = MED_FREQ,
@@ -50,8 +56,31 @@ var/list/radiochannels = list(
"AI Private" = AI_FREQ,
"Entertainment" = ENT_FREQ,
"Medical (I)" = MED_I_FREQ,
"Security (I)" = SEC_I_FREQ,
"Ship" = SHIP_FREQ
"Security (I)" = SEC_I_FREQ
)
var/list/reverseradiochannels = list(
"[PUB_FREQ]" = "Common",
"[HAIL_FREQ]" = "Hailing",
"[SCI_FREQ]" = "Science",
"[COMM_FREQ]" = "Command",
"[MED_FREQ]" = "Medical",
"[ENG_FREQ]" = "Engineering",
"[SEC_FREQ]" = "Security",
"[PEN_FREQ]" = "Penal",
"[ERT_FREQ]" = "Response Team",
"[DTH_FREQ]" = "Special Ops",
"[SYND_FREQ]" = "Mercenary",
"[NINJ_FREQ]" = "Ninja",
"[BLSP_FREQ]" = "Bluespace",
"[BURG_FREQ]" = "Burglar",
"[RAID_FREQ]" = "Raider",
"[SUP_FREQ]" = "Operations",
"[SRV_FREQ]" = "Service",
"[AI_FREQ]" = "AI Private",
"[ENT_FREQ]" = "Entertainment",
"[MED_I_FREQ]" = "Medical (I)",
"[SEC_I_FREQ]" = "Security (I)"
)
// The assoc variants are separate lists because they need the keys to be strings, but some code expects numbers.
@@ -80,7 +109,8 @@ var/list/ANTAG_FREQS_ASSOC = list(
"[SYND_FREQ]" = TRUE,
"[RAID_FREQ]" = TRUE,
"[NINJ_FREQ]" = TRUE,
"[BURG_FREQ]" = TRUE
"[BURG_FREQ]" = TRUE,
"[BLSP_FREQ]" = TRUE
)
//Department channels, arranged lexically
@@ -93,8 +123,7 @@ var/list/DEPT_FREQS = list(
SCI_FREQ,
SRV_FREQ,
SUP_FREQ,
ENT_FREQ,
SHIP_FREQ
ENT_FREQ
)
var/list/DEPT_FREQS_ASSOC = list(
@@ -110,8 +139,11 @@ var/list/DEPT_FREQS_ASSOC = list(
)
#define TRANSMISSION_WIRE 0 // Wired transmission, unused at the moment
#define TRANSMISSION_RADIO 1
#define TRANSMISSION_SUBSPACE 2
#define TRANSMISSION_RADIO 1 // Default radiowave transmission
#define TRANSMISSION_SUBSPACE 2 // Subspace transmission (headsets)
#define TRANSMISSION_SUPERSPACE 3 // Independent / CentCom radios only
#define RADIO_NO_Z_LEVEL_RESTRICTION 0
/* filters */
//When devices register with the radio controller, they might register under a certain filter.

View File

@@ -0,0 +1,16 @@
// Each cell in a spatial_grid is this many turfs in length and width
#define SPATIAL_GRID_CELLSIZE 17
#define SPATIAL_GRID_CELLS_PER_SIDE(world_bounds) ROUND_UP((world_bounds) / SPATIAL_GRID_CELLSIZE)
#define SPATIAL_GRID_CHANNELS 2
// grid contents channels
// Everything that is hearing sensitive is stored in this channel
#define SPATIAL_GRID_CONTENTS_TYPE_HEARING RECURSIVE_CONTENTS_HEARING_SENSITIVE
// Every movable that has a client in it is stored in this channel
#define SPATIAL_GRID_CONTENTS_TYPE_CLIENTS RECURSIVE_CONTENTS_CLIENT_MOBS
// Whether movable is itself or containing something which should be in one of the spatial grid channels
#define HAS_SPATIAL_GRID_CONTENTS(movable) (movable.important_recursive_contents && (movable.important_recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE] || movable.important_recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS]))

144
code/__defines/traits.dm Normal file
View File

@@ -0,0 +1,144 @@
#define SIGNAL_ADDTRAIT(trait_ref) "addtrait [trait_ref]"
#define SIGNAL_REMOVETRAIT(trait_ref) "removetrait [trait_ref]"
// trait accessor defines
#define ADD_TRAIT(target, trait, source) \
do { \
var/list/_L; \
if (!target.status_traits) { \
target.status_traits = list(); \
_L = target.status_traits; \
_L[trait] = list(source); \
SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
} else { \
_L = target.status_traits; \
if (_L[trait]) { \
_L[trait] |= list(source); \
} else { \
_L[trait] = list(source); \
SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
} \
} \
} while (0)
#define REMOVE_TRAIT(target, trait, sources) \
do { \
var/list/_L = target.status_traits; \
var/list/_S; \
if (sources && !islist(sources)) { \
_S = list(sources); \
} else { \
_S = sources\
}; \
if (_L && _L[trait]) { \
for (var/_T in _L[trait]) { \
if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \
_L[trait] -= _T \
} \
};\
if (!length(_L[trait])) { \
_L -= trait; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
}; \
if (!length(_L)) { \
target.status_traits = null \
}; \
} \
} while (0)
#define REMOVE_TRAIT_NOT_FROM(target, trait, sources) \
do { \
var/list/_traits_list = target.status_traits; \
var/list/_sources_list; \
if (sources && !islist(sources)) { \
_sources_list = list(sources); \
} else { \
_sources_list = sources\
}; \
if (_traits_list && _traits_list[trait]) { \
for (var/_trait_source in _traits_list[trait]) { \
if (!(_trait_source in _sources_list)) { \
_traits_list[trait] -= _trait_source \
} \
};\
if (!length(_traits_list[trait])) { \
_traits_list -= trait; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
}; \
if (!length(_traits_list)) { \
target.status_traits = null \
}; \
} \
} while (0)
#define REMOVE_TRAITS_NOT_IN(target, sources) \
do { \
var/list/_L = target.status_traits; \
var/list/_S = sources; \
if (_L) { \
for (var/_T in _L) { \
_L[_T] &= _S;\
if (!length(_L[_T])) { \
_L -= _T; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T), _T); \
}; \
};\
if (!length(_L)) { \
target.status_traits = null\
};\
}\
} while (0)
#define REMOVE_TRAITS_IN(target, sources) \
do { \
var/list/_L = target.status_traits; \
var/list/_S = sources; \
if (sources && !islist(sources)) { \
_S = list(sources); \
} else { \
_S = sources\
}; \
if (_L) { \
for (var/_T in _L) { \
_L[_T] -= _S;\
if (!length(_L[_T])) { \
_L -= _T; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T)); \
}; \
};\
if (!length(_L)) { \
target.status_traits = null\
};\
}\
} while (0)
#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
#define HAS_TRAIT_FROM_ONLY(target, trait, source) (\
target.status_traits ?\
(target.status_traits[trait] ?\
((source in target.status_traits[trait]) && (length(target.status_traits) == 1))\
: FALSE)\
: FALSE)
#define HAS_TRAIT_NOT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (length(target.status_traits[trait] - source) > 0) : FALSE) : FALSE)
// common trait sources
#define TRAIT_GENERIC "generic"
#define GENERIC_ITEM_TRAIT "generic_item"
#define UNCONSCIOUS_TRAIT "unconscious"
#define EYE_DAMAGE "eye_damage"
#define EAR_DAMAGE "ear_damage"
#define GENETIC_MUTATION "genetic"
#define OBESITY "obesity"
#define MAGIC_TRAIT "magic"
#define TRAUMA_TRAIT "trauma"
/// cannot be removed without admin intervention
#define ROUNDSTART_TRAIT "roundstart"
#define JOB_TRAIT "job"
#define INNATE_TRAIT "innate"
//important_recursive_contents traits
/*
* Used for movables that need to be updated, via COMSIG_ENTER_AREA and COMSIG_EXIT_AREA, when transitioning areas.
* Use [/atom/movable/proc/become_area_sensitive(trait_source)] to properly enable it. How you remove it isn't as important.
*/
#define TRAIT_AREA_SENSITIVE "area-sensitive"
// every hearing sensitive atom has this trait
#define TRAIT_HEARING_SENSITIVE "hearing_sensitive"

View File

@@ -39,83 +39,6 @@
return 0 //not in range and not telekinetic
// Like view but bypasses luminosity check
/proc/hear(var/range, var/atom/source)
var/lum = source.luminosity
source.luminosity = 6
var/list/heard = view(range, source)
source.luminosity = lum
return heard
/proc/circlerange(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/atom/T in range(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
//turfs += centerturf
return turfs
/proc/circleview(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
var/list/atoms = new/list()
var/rsq = radius * (radius+0.5)
for(var/atom/A in view(radius, centerturf))
var/dx = A.x - centerturf.x
var/dy = A.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
atoms += A
//turfs += centerturf
return atoms
/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj)
var/dx = Loc1.x - Loc2.x
var/dy = Loc1.y - Loc2.y
var/dist = sqrt(dx**2 + dy**2)
return dist
/proc/circlerangeturfs(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
if(radius == 1)
return list(centerturf)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/turf/T in range(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
return turfs
/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()?
var/turf/centerturf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/turf/T in view(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
return turfs
// Will recursively loop through an atom's locs until it finds the atom loc above a turf or its target_atom
/proc/recursive_loc_turf_check(var/atom/O, var/recursion_limit = 3, var/atom/target_atom)
if(recursion_limit <= 0 || isturf(O.loc) || O == target_atom)
@@ -125,147 +48,6 @@
recursion_limit--
return recursive_loc_turf_check(O, recursion_limit)
// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents.
// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects,
// being unable to hear people due to being in a box within a bag.
// Does not return list, as list is passed as reference.
/proc/recursive_content_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_mobs = 1, var/include_objects = 1)
if(!recursion_limit)
return
for(var/I in O.contents)
if(ismob(I))
if(!sight_check || isInSight(I, O))
recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects)
if(include_mobs)
if(client_check)
var/mob/M = I
if(M.client)
L += M
else
L += I
else if(isobj(I))
if(!sight_check || isInSight(I, O))
recursive_content_check(I, L, recursion_limit - 1, client_check, sight_check, include_mobs, include_objects)
if(include_objects)
L += I
// Returns a list of mobs and/or objects in range of R from source. Used in radio and say code.
/proc/get_mobs_in_radio_ranges(var/list/obj/item/device/radio/radios)
set background = 1
. = list()
// Returns a list of mobs who can hear any of the radios given in @radios
var/list/speaker_coverage = list()
for(var/obj/item/device/radio/R in radios)
if(R)
//Cyborg checks. Receiving message uses a bit of cyborg's charge.
var/obj/item/device/radio/borg/BR = R
if(istype(BR) && BR.myborg)
var/mob/living/silicon/robot/borg = BR.myborg
var/datum/robot_component/CO = borg.get_component("radio")
if(!CO)
continue //No radio component (Shouldn't happen)
if(!borg.is_component_functioning("radio") || !borg.cell_use_power(CO.active_usage))
continue //No power.
var/turf/speaker = get_turf(R)
if(speaker)
for(var/turf/T in hear(R.canhear_range,speaker))
speaker_coverage[T] = T
var/list/listeners = player_list.Copy()
for(var/mob/M as anything in player_list)
if(M.old_mob)
listeners += M.old_mob
// Try to find all the players who can hear the message
for(var/mob/M as anything in listeners)
if(M.can_hear_radio(speaker_coverage))
. += M
/proc/get_mobs_or_objs_in_view(turf/T, range, list/mobs, list/objs, checkghosts = GHOSTS_ALL_HEAR)
var/list/hear = list()
DVIEW(hear, range, T, INVISIBILITY_MAXIMUM)
var/list/hearturfs = list()
if(islist(mobs))
for(var/mob/M in hear)
mobs[M] = TRUE
hearturfs[get_turf(M)] = TRUE
for(var/mob/M in player_list)
#ifdef UNIT_TEST
if(istype(M, /mob/living/test))
mobs[M] = TRUE
continue
#endif
if(checkghosts == GHOSTS_ALL_HEAR && M.stat == DEAD && !isnewplayer(M) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))
mobs[M] = TRUE
continue
if(M.loc && hearturfs[get_turf(M)])
mobs[M] = TRUE
if(islist(objs))
for(var/obj/O in hear)
objs[O] = TRUE
hearturfs[get_turf(O)] = TRUE
for(var/obj/O in listening_objects)
if(O.loc && hearturfs[get_turf(O)])
objs[O] = TRUE
proc
inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5)
var/turf/T
if(X1==X2)
if(Y1==Y2)
return 1 //Light cannot be blocked on same tile
else
var/s = SIGN(Y2-Y1)
Y1+=s
while(Y1!=Y2)
T=locate(X1,Y1,Z)
if(T.opacity)
return 0
Y1+=s
else
var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1))
var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles
var/signX = SIGN(X2-X1)
var/signY = SIGN(Y2-Y1)
if(X1<X2)
b+=m
while(X1!=X2 || Y1!=Y2)
if(round(m*X1+b-Y1))
Y1+=signY //Line exits tile vertically
else
X1+=signX //Line exits tile horizontally
T=locate(X1,Y1,Z)
if(T.opacity)
return 0
return 1
proc/isInSight(var/atom/A, var/atom/B)
var/turf/Aturf = get_turf(A)
var/turf/Bturf = get_turf(B)
if(!Aturf || !Bturf)
return 0
if(inLineOfSight(Aturf.x,Aturf.y, Bturf.x,Bturf.y,Aturf.z))
return 1
else
return 0
/proc/get_cardinal_step_away(atom/start, atom/finish) //returns the position of a step from start away from finish, in one of the cardinal directions
//returns only NORTH, SOUTH, EAST, or WEST
var/dx = finish.x - start.x

View File

@@ -28,6 +28,7 @@ var/global/list/the_station_areas = list()
var/global/list/implants = list()
var/global/list/turfs = list() //list of all turfs
var/global/list/station_turfs = list()
var/global/list/areas_by_type = list()
var/global/list/all_areas = list()

View File

@@ -171,3 +171,50 @@
/// Value or the next multiple of divisor in a positive direction. Ceilm(-1.5, 0.3) = -1.5 , Ceilm(-1.5, 0.4) = -1.2
#define Ceilm(value, divisor) ( -round(-(value) / (divisor)) * (divisor) )
/**
* Get a list of turfs in a line from `starting_atom` to `ending_atom`.
*
* Uses the ultra-fast [Bresenham Line-Drawing Algorithm](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm).
*/
/proc/get_line(atom/starting_atom, atom/ending_atom)
var/current_x_step = starting_atom.x // start at X and Y, then add 1 or -1 to these to get every turf from start to end
var/current_y_step = starting_atom.y
var/starting_z = starting_atom.z
var/list/line = list(get_turf(starting_atom))
var/x_distance = ending_atom.x - current_x_step
var/y_distance = ending_atom.y - current_y_step
var/abs_x_distance = abs(x_distance)
var/abs_y_distance = abs(y_distance)
var/x_distance_sign = SIGN(x_distance)
var/y_distance_sign = SIGN(y_distance)
var/x = abs_x_distance >> 1
var/y = abs_y_distance >> 1
if (abs_x_distance >= abs_y_distance)
for (var/distance_counter in 0 to (abs_x_distance - 1))
y += abs_y_distance
if(y >= abs_x_distance) // Every abs_y_distance steps, step once in y direction
y -= abs_x_distance
current_y_step += y_distance_sign
current_x_step += x_distance_sign // Step in x direction
line += locate(current_x_step, current_y_step, starting_z)
else
for (var/distance_counter in 0 to (abs_y_distance - 1))
x += abs_x_distance
if(x >= abs_y_distance)
x -= abs_y_distance
current_x_step += x_distance_sign
current_y_step += y_distance_sign
line += locate(current_x_step, current_y_step, starting_z)
return line

View File

@@ -232,7 +232,7 @@ Proc for attack log creation, because really why not
break
//update our pda and id if we have them on our person
var/list/searching = GetAllContents(searchDepth = 3)
var/list/searching = GetAllContents()
var/search_id = 1
var/search_pda = 1

View File

@@ -0,0 +1,374 @@
/turf
///what /mob/abstract/oranges_ear instance is already assigned to us as there should only ever be one.
///used for guaranteeing there is only one oranges_ear per turf when assigned, speeds up view() iteration
var/mob/abstract/oranges_ear/assigned_oranges_ear
/** # Oranges Ear
*
* turns out view() spends a significant portion of its processing time generating lists of contents of viewable turfs which includes EVERYTHING on it visible
* and the turf itself. there is an optimization to view() which makes it only generate lists of a certain atom type - this system takes advantage of that.
* a fuckton of these are generated as part of its SS's init and stored in a list, when requested for a list of movables returned by the spatial grid or by some
* superset of the final output that must be narrowed down by view() one of these gets put on every turf that contains the movables that need filtering
* and each is given references to the movables they represent. that way you can do for(var/mob/abstract/oranges_ear/ear in view(...)) and check what they reference
* as opposed to for(var/atom/movable/target in view(...)) and checking if they have the properties you want which leads to much larger lists generated by view()
* and also leads to iterating through more movables to filter them.
*
* TLDR: iterating through just mobs is much faster than all movables when iterating through view(), this system leverages that to boost speed
* enough to offset the cost of allocating the mobs
*
* named because the idea was first made by oranges and i didnt know what else to call it (note that this system was originally made for get_hearers_in_view())
*/
/mob/abstract/oranges_ear
icon_state = null
invisibility = 0
mouse_opacity = 0
faction = null
screens = null
/// references to everything "on" the turf we are assigned to, that we care about. populated in assign() and cleared in unassign().
/// movables iside of other movables count as being "on" if they have get_turf(them) == our turf. intentionally not a lazylist
var/list/references = list()
/mob/abstract/oranges_ear/Initialize(mapload)
SHOULD_CALL_PARENT(FALSE)
return INITIALIZE_HINT_NORMAL
/mob/abstract/oranges_ear/Destroy(force)
var/old_length = length(SSspatial_grid.pregenerated_oranges_ears)
SSspatial_grid.pregenerated_oranges_ears -= src
if(length(SSspatial_grid.pregenerated_oranges_ears) < old_length)
SSspatial_grid.number_of_oranges_ears -= 1
var/turf/our_loc = get_turf(src)
if(our_loc && our_loc.assigned_oranges_ear == src)
our_loc.assigned_oranges_ear = null
. = ..()
/mob/abstract/oranges_ear/Move()
SHOULD_CALL_PARENT(FALSE)
crash_with("SOMEHOW A /mob/abstract/oranges_ear MOVED")
return FALSE
/mob/abstract/oranges_ear/forceMove(atom/destination)
SHOULD_CALL_PARENT(FALSE)
crash_with("SOMEHOW A /mob/abstract/oranges_ear MOVED")
return FALSE
/mob/abstract/oranges_ear/Bump()
SHOULD_CALL_PARENT(FALSE)
return FALSE
///clean this oranges_ear up for future use
/mob/abstract/oranges_ear/proc/unassign()
var/turf/turf_loc = loc
turf_loc.assigned_oranges_ear = null//trollface. our loc should ALWAYS be a turf, no exceptions. if it isnt then this doubles as an error message ;)
loc = null
references.Cut()
/**
* returns every hearaing movable in view to the turf of source not taking into account lighting
* useful when you need to maintain always being able to hear something if a sound is emitted from it and you can see it (and youre in range).
* otherwise this is just a more expensive version of get_hearers_in_LOS()
*
* * view_radius - what radius search circle we are using, worse performance as this increases
* * source - object at the center of our search area. everything in get_turf(source) is guaranteed to be part of the search area
*/
/proc/get_hearers_in_view(view_radius, atom/source)
var/turf/center_turf = get_turf(source)
if(!center_turf)
return
. = list()
if(view_radius <= 0)//special case for if only source cares
for(var/atom/movable/target as anything in center_turf)
var/list/recursive_contents = target.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE]
if(recursive_contents)
. += recursive_contents
return .
var/list/hearables_from_grid = SSspatial_grid.orthogonal_range_search(source, RECURSIVE_CONTENTS_HEARING_SENSITIVE, view_radius)
if(!length(hearables_from_grid))//we know that something is returned by the grid, but we dont know if we need to actually filter down the output
return .
var/list/assigned_oranges_ears = SSspatial_grid.assign_oranges_ears(hearables_from_grid)
var/old_luminosity = center_turf.luminosity
center_turf.luminosity = 6 //man if only we had an inbuilt dview()
//this is the ENTIRE reason all this shit is worth it due to how view() works and can be optimized
//view() constructs lists of viewed atoms by default and specifying a specific type of atom to look for limits the lists it constructs to those of that
//primitive type and then when the view operation is completed the output is then typechecked to only iterate through objects in view with the same
//typepath. by assigning one /mob/abstract/oranges_ear to every turf with hearable atoms on it and giving them references to each one means that:
//1. view() only constructs lists of atoms with the mob primitive type and
//2. the mobs returned by view are fast typechecked to only iterate through /mob/abstract/oranges_ear mobs, which guarantees at most one per turf
//on a whole this can outperform iterating through all movables in view() by ~2x especially when hearables are a tiny percentage of movables in view
for(var/mob/abstract/oranges_ear/ear in view(view_radius, center_turf))
. += ear.references
for(var/mob/abstract/oranges_ear/remaining_ear as anything in assigned_oranges_ears)//we need to clean up our mess
remaining_ear.unassign()
center_turf.luminosity = old_luminosity
return .
/**
* Returns a list of movable atoms that are hearing sensitive in view_radius and line of sight to source
* the majority of the work is passed off to the spatial grid if view_radius > 0
* because view() isnt a raycasting algorithm, this does not hold symmetry to it. something in view might not be hearable with this.
* if you want that use get_hearers_in_view() - however thats significantly more expensive
*
* * view_radius - what radius search circle we are using, worse performance as this increases but not as much as it used to
* * source - object at the center of our search area. everything in get_turf(source) is guaranteed to be part of the search area
*/
/proc/get_hearers_in_LOS(view_radius, atom/source)
var/turf/center_turf = get_turf(source)
if(!center_turf)
return
if(view_radius <= 0)//special case for if only source cares
. = list()
for(var/atom/movable/target as anything in center_turf)
var/list/hearing_contents = target.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE]
if(hearing_contents)
. += hearing_contents
return
. = SSspatial_grid.orthogonal_range_search(source, SPATIAL_GRID_CONTENTS_TYPE_HEARING, view_radius)
for(var/atom/movable/target as anything in .)
var/turf/target_turf = get_turf(target)
var/distance = get_dist(center_turf, target_turf)
if(distance > view_radius)
. -= target
continue
else if(distance < 2) //we should always be able to see something 0 or 1 tiles away
continue
//this turf search algorithm is the worst scaling part of this proc, scaling worse than view() for small-moderate ranges and > 50 length contents_to_return
//luckily its significantly faster than view for large ranges in large spaces and/or relatively few contents_to_return
//i can do things that would scale better, but they would be slower for low volume searches which is the vast majority of the current workload
//maybe in the future a high volume algorithm would be worth it
var/turf/inbetween_turf = center_turf
//this is the lowest overhead way of doing a loop in dm other than a goto. distance is guaranteed to be >= steps taken to target by this algorithm
for(var/step_counter in 1 to distance)
inbetween_turf = get_step_towards(inbetween_turf, target_turf)
if(inbetween_turf == target_turf)//we've gotten to target's turf without returning due to turf opacity, so we must be able to see target
break
if(inbetween_turf.opacity)//this turf or something on it is opaque so we cant see through it
. -= target
break
/proc/get_hearers_in_radio_ranges(list/obj/item/device/radio/radios)
. = list()
// Returns a list of mobs who can hear any of the radios given in @radios
for(var/obj/item/device/radio/radio in radios)
. |= get_hearers_in_LOS(radio.canhear_range, radio, FALSE)
///Calculate if two atoms are in sight, returns TRUE or FALSE
/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5)
var/turf/T
if(X1==X2)
if(Y1==Y2)
return TRUE //Light cannot be blocked on same tile
else
var/s = SIGN(Y2-Y1)
Y1+=s
while(Y1!=Y2)
T=locate(X1,Y1,Z)
if(T.opacity)
return FALSE
Y1+=s
else
var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1))
var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles
var/signX = SIGN(X2-X1)
var/signY = SIGN(Y2-Y1)
if(X1<X2)
b+=m
while(X1!=X2 || Y1!=Y2)
if(round(m*X1+b-Y1))
Y1+=signY //Line exits tile vertically
else
X1+=signX //Line exits tile horizontally
T=locate(X1,Y1,Z)
if(T.opacity)
return FALSE
return TRUE
/proc/isInSight(atom/first_atom, atom/second_atom)
var/turf/first_turf = get_turf(first_atom)
var/turf/second_turf = get_turf(second_atom)
if(!first_turf || !second_turf)
return FALSE
return inLineOfSight(first_turf.x, first_turf.y, second_turf.x, second_turf.y, first_turf.z)
///Returns all atoms present in a circle around the center
/proc/circle_range(center = usr,radius = 3)
var/turf/center_turf = get_turf(center)
var/list/atoms = new/list()
var/rsq = radius * (radius + 0.5)
for(var/atom/checked_atom as anything in range(radius, center_turf))
var/dx = checked_atom.x - center_turf.x
var/dy = checked_atom.y - center_turf.y
if(dx * dx + dy * dy <= rsq)
atoms += checked_atom
return atoms
///Returns all atoms present in a circle around the center but uses view() instead of range() (Currently not used)
/proc/circle_view(center=usr,radius=3)
var/turf/center_turf = get_turf(center)
var/list/atoms = new/list()
var/rsq = radius * (radius + 0.5)
for(var/atom/checked_atom as anything in view(radius, center_turf))
var/dx = checked_atom.x - center_turf.x
var/dy = checked_atom.y - center_turf.y
if(dx * dx + dy * dy <= rsq)
atoms += checked_atom
return atoms
///Returns the distance between two atoms
/proc/get_dist_euclidian(atom/first_location as turf|mob|obj, atom/second_location as turf|mob|obj)
var/dx = first_location.x - second_location.x
var/dy = first_location.y - second_location.y
var/dist = sqrt(dx ** 2 + dy ** 2)
return dist
///Returns a list of turfs around a center based on RANGE_TURFS()
/proc/circle_range_turfs(center = usr, radius = 3)
var/turf/center_turf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius + 0.5)
for(var/turf/checked_turf as anything in RANGE_TURFS(radius, center_turf))
var/dx = checked_turf.x - center_turf.x
var/dy = checked_turf.y - center_turf.y
if(dx * dx + dy * dy <= rsq)
turfs += checked_turf
return turfs
///Returns a list of turfs around a center based on view()
/proc/circle_view_turfs(center=usr,radius=3) //Is there even a diffrence between this proc and circle_range_turfs()?
var/turf/center_turf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius + 0.5)
for(var/turf/checked_turf in view(radius, center_turf))
var/dx = checked_turf.x - center_turf.x
var/dy = checked_turf.y - center_turf.y
if(dx * dx + dy * dy <= rsq)
turfs += checked_turf
return turfs
/**
* Get a bounding box of a list of atoms.
*
* Arguments:
* - atoms - List of atoms. Can accept output of view() and range() procs.
*
* Returns: list(x1, y1, x2, y2)
*/
/proc/get_bbox_of_atoms(list/atoms)
var/list/list_x = list()
var/list/list_y = list()
for(var/_a in atoms)
var/atom/a = _a
list_x += a.x
list_y += a.y
return list(
min(list_x),
min(list_y),
max(list_x),
max(list_y))
/// Like view but bypasses luminosity check
/proc/get_hear(range, atom/source)
var/lum = source.luminosity
source.luminosity = 6
. = view(range, source)
source.luminosity = lum
///Returns the open turf next to the center in a specific direction
/proc/get_open_turf_in_dir(atom/center, dir)
var/turf/T = get_ranged_target_turf(center, dir, 1)
if(istype(T) && !T.density)
return T
///Returns a list with all the adjacent open turfs. Clears the list of nulls in the end.
/proc/get_adjacent_open_turfs(atom/center)
. = list(
get_open_turf_in_dir(center, NORTH),
get_open_turf_in_dir(center, SOUTH),
get_open_turf_in_dir(center, EAST),
get_open_turf_in_dir(center, WEST)
)
listclearnulls(.)
///Returns a list with all the adjacent areas by getting the adjacent open turfs
/proc/get_adjacent_open_areas(atom/center)
. = list()
var/list/adjacent_turfs = get_adjacent_open_turfs(center)
for(var/near_turf in adjacent_turfs)
. |= get_area(near_turf)
/**
* Returns a list with the names of the areas around a center at a certain distance
* Returns the local area if no distance is indicated
* Returns an empty list if the center is null
**/
/proc/get_areas_in_range(distance = 0, atom/center = usr)
if(!distance)
var/turf/center_turf = get_turf(center)
return center_turf ? list(center_turf.loc) : list()
if(!center)
return list()
var/list/turfs = RANGE_TURFS(distance, center)
var/list/areas = list()
for(var/turf/checked_turf as anything in turfs)
areas |= checked_turf.loc
return areas
///Returns a list of all areas that are adjacent to the center atom's area, clear the list of nulls at the end.
/proc/get_adjacent_areas(atom/center)
. = list(
get_area(get_ranged_target_turf(center, NORTH, 1)),
get_area(get_ranged_target_turf(center, SOUTH, 1)),
get_area(get_ranged_target_turf(center, EAST, 1)),
get_area(get_ranged_target_turf(center, WEST, 1))
)
listclearnulls(.)
///Checks if the mob provided (must_be_alone) is alone in an area
/proc/alone_in_area(area/the_area, mob/must_be_alone, check_type = /mob/living/carbon)
var/area/our_area = get_area(the_area)
for(var/carbon in living_mob_list)
if(!istype(carbon, check_type))
continue
if(carbon == must_be_alone)
continue
if(our_area == get_area(carbon))
return FALSE
return TRUE

View File

@@ -286,6 +286,7 @@
SearchVar(ghostteleportlocs)
SearchVar(centcom_areas)
SearchVar(the_station_areas)
SearchVar(station_turfs)
SearchVar(implants)
SearchVar(turfs)
SearchVar(all_species)
@@ -618,7 +619,6 @@
SearchVar(syndicate_elite_shuttle_timeleft)
SearchVar(recentmessages)
SearchVar(message_delay)
SearchVar(telecomms_list)
SearchVar(word_to_uristrune_table)
SearchVar(slot_flags_enumeration)
SearchVar(BUMP_TELEPORTERS)

View File

@@ -57,6 +57,8 @@ if(Datum.isprocessing) {\
var/list/obj/machinery/hologram/holopad/all_holopads = list()
var/list/all_status_displays = list() // Note: This contains both ai_status_display and status_display.
var/list/gravity_generators = list()
var/list/obj/machinery/telecomms/all_telecomms = list()
var/list/obj/machinery/telecomms/all_receivers = list()
var/list/rcon_smes_units = list()
var/list/rcon_smes_units_by_tag = list()
@@ -76,8 +78,10 @@ if(Datum.isprocessing) {\
all_cameras = SSmachinery.all_cameras
all_holopads = SSmachinery.all_holopads
recipe_datums = SSmachinery.recipe_datums
breaker_boxes = breaker_boxes
all_sensors = all_sensors
breaker_boxes = SSmachinery.breaker_boxes
all_sensors = SSmachinery.all_sensors
all_telecomms = SSmachinery.all_telecomms
all_receivers = SSmachinery.all_receivers
current_step = SSMACHINERY_PIPENETS
/datum/controller/subsystem/machinery/New()

View File

@@ -136,6 +136,17 @@ var/datum/controller/subsystem/radio/SSradio
return 1
/datum/controller/subsystem/radio/proc/remove_object_all(obj/device)
for(var/freq in frequencies)
SSradio.remove_object(device, text2num(freq))
/datum/controller/subsystem/radio/proc/get_devices(freq, filter = RADIO_DEFAULT)
var/datum/radio_frequency/frequency = frequencies[num2text(freq)]
if(!frequency)
return
return frequency.devices["[filter]"]
/datum/controller/subsystem/radio/proc/return_frequency(new_frequency)
var/f_text = num2text(new_frequency)
var/datum/radio_frequency/frequency = frequencies[f_text]
@@ -147,6 +158,12 @@ var/datum/controller/subsystem/radio/SSradio
return frequency
// Used to test connectivity to the telecomms network.
/datum/controller/subsystem/radio/proc/telecomms_ping(obj/O, test_freq = PUB_FREQ)
var/datum/signal/subspace/testsig = new(O, test_freq)
for (var/obj/machinery/telecomms/R in SSmachinery.all_receivers)
if(R.receive_range(testsig) >= 0)
return TRUE
// Some misc procs not technically part of the subsystem, but are related.
@@ -155,7 +172,6 @@ var/datum/controller/subsystem/radio/SSradio
return null
/proc/frequency_span_class(var/frequency)
. = "radio"
var/fstr = "[frequency]"
// Antags!
if (ANTAG_FREQS_ASSOC[fstr])
@@ -168,29 +184,50 @@ var/datum/controller/subsystem/radio/SSradio
// department radio formatting
switch (frequency)
if (COMM_FREQ) // command
. = "comradio"
return "comradio"
if (AI_FREQ) // AI Private
. = "airadio"
return "airadio"
if (SEC_FREQ,SEC_I_FREQ)
. = "secradio"
return "secradio"
if (PEN_FREQ)
. = "penradio"
return "penradio"
if (ENG_FREQ)
. = "engradio"
return "engradio"
if (SCI_FREQ)
. = "sciradio"
return "sciradio"
if (MED_FREQ,MED_I_FREQ)
. = "medradio"
return"medradio"
if (SUP_FREQ) // cargo
. = "supradio"
return "supradio"
if (SRV_FREQ) // service
. = "srvradio"
return "srvradio"
if (ENT_FREQ) //entertainment
. = "entradio"
return "entradio"
if (BLSP_FREQ)
. = "bluespaceradio"
if (SHIP_FREQ)
. = "shipradio"
else
return "bluespaceradio"
if (HAIL_FREQ)
return "hailradio"
if(DEPT_FREQS_ASSOC[fstr])
. = "deptradio"
return "deptradio"
for(var/channel in AWAY_FREQS_ASSIGNED)
if(AWAY_FREQS_ASSIGNED[channel] == frequency)
return "shipradio"
return "radio"
/proc/assign_away_freq(channel)
if (!AWAY_FREQS_UNASSIGNED.len)
return FALSE
if (channel in AWAY_FREQS_ASSIGNED)
return AWAY_FREQS_ASSIGNED[channel]
var/freq = pick_n_take(AWAY_FREQS_UNASSIGNED)
AWAY_FREQS_ASSIGNED[channel] = freq
radiochannels[channel] = freq
reverseradiochannels["[freq]"] = channel
ALL_RADIO_CHANNELS[channel] = TRUE
return freq

View File

@@ -0,0 +1,611 @@
///the subsystem creates this many [/mob/abstract/oranges_ear] mob instances during init. allocations that require more than this create more.
#define NUMBER_OF_PREGENERATED_ORANGES_EARS 2500
// macros meant specifically to add/remove movables from the hearing_contents and client_contents lists of
// /datum/spatial_grid_cell, when empty they become references to a single list in SSspatial_grid and when filled they become their own list
// this is to save memory without making them lazylists as that slows down iteration through them
#define GRID_CELL_ADD(cell_contents_list, movable_or_list) \
if(!length(cell_contents_list)) { \
cell_contents_list = list(); \
cell_contents_list += movable_or_list; \
} else { \
cell_contents_list += movable_or_list; \
};
#define GRID_CELL_SET(cell_contents_list, movable_or_list) \
if(!length(cell_contents_list)) { \
cell_contents_list = list(); \
cell_contents_list += movable_or_list; \
} else { \
cell_contents_list |= movable_or_list; \
};
//dont use these outside of SSspatial_grid's scope use the procs it has for this purpose
#define GRID_CELL_REMOVE(cell_contents_list, movable_or_list) \
cell_contents_list -= movable_or_list; \
if(!length(cell_contents_list)) {\
cell_contents_list = dummy_list; \
};
/**
* # Spatial Grid Cell
*
* used by [/datum/controller/subsystem/spatial_grid] to cover every z level so that the coordinates of every turf in the world corresponds to one of these in
* the subsystems list of grid cells by z level. each one of these contains content lists holding all atoms meeting a certain criteria that is in our borders.
* these datums shouldnt have significant behavior, they should just hold data. the lists are filled and emptied by the subsystem.
*/
/datum/spatial_grid_cell
///our x index in the list of cells. this is our index inside of our row list
var/cell_x
///our y index in the list of cells. this is the index of our row list inside of our z level grid
var/cell_y
///which z level we belong to, corresponding to the index of our gridmap in SSspatial_grid.grids_by_z_level
var/cell_z
//every data point in a grid cell is separated by usecase
//when empty, the contents lists of these grid cell datums are just references to a dummy list from SSspatial_grid
//this is meant to allow a great compromise between memory usage and speed.
//now orthogonal_range_search() doesnt need to check if the list is null and each empty list is taking 12 bytes instead of 24
//the only downside is that it needs to be switched over to a new list when it goes from 0 contents to > 0 contents and switched back on the opposite case
///every hearing sensitive movable inside this cell
var/list/hearing_contents
///every client possessed mob inside this cell
var/list/client_contents
/datum/spatial_grid_cell/New(cell_x, cell_y, cell_z)
. = ..()
src.cell_x = cell_x
src.cell_y = cell_y
src.cell_z = cell_z
//cache for sanic speed (lists are references anyways)
var/list/dummy_list = SSspatial_grid.dummy_list
if(length(dummy_list))
dummy_list.Cut()
crash_with("SSspatial_grid.dummy_list had something inserted into it at some point! this is a problem as it is supposed to stay empty")
hearing_contents = dummy_list
client_contents = dummy_list
/datum/spatial_grid_cell/Destroy(force, ...)
if(force)//the response to someone trying to qdel this is a right proper fuck you
crash_with("dont try to destroy spatial grid cells without a good reason. if you need to do it use force")
return
. = ..()
/**
* # Spatial Grid
*
* a gamewide grid of spatial_grid_cell datums, each "covering" [SPATIAL_GRID_CELLSIZE] ^ 2 turfs.
* each spatial_grid_cell datum stores information about what is inside its covered area, so that searches through that area dont have to literally search
* through all turfs themselves to know what is within it since view() calls are expensive, and so is iterating through stuff you dont want.
* this allows you to only go through lists of what you want very cheaply.
*
* you can also register to objects entering and leaving a spatial cell, this allows you to do things like stay idle until a player enters, so you wont
* have to use expensive view() calls or iteratite over the global list of players and call get_dist() on every one. which is fineish for a few things, but is
* k * n operations for k objects iterating through n players.
*
* currently this system is only designed for searching for relatively uncommon things, small subsets of /atom/movable.
* dont add stupid shit to the cells please, keep the information that the cells store to things that need to be searched for often
*
* as of right now this system operates on a subset of the important_recursive_contents list for atom/movable, specifically
* [RECURSIVE_CONTENTS_HEARING_SENSITIVE] and [RECURSIVE_CONTENTS_CLIENT_MOBS] because both are those are both 1. important and 2. commonly searched for
*/
/var/datum/controller/subsystem/spatial_grid/SSspatial_grid
/datum/controller/subsystem/spatial_grid
name = "Spatial Grid"
flags = SS_NO_FIRE
///list of the spatial_grid_cell datums per z level, arranged in the order of y index then x index
var/list/grids_by_z_level = list()
///everything that spawns before us is added to this list until we initialize
var/list/waiting_to_add_by_type = list(RECURSIVE_CONTENTS_HEARING_SENSITIVE = list(), RECURSIVE_CONTENTS_CLIENT_MOBS = list())
var/cells_on_x_axis = 0
var/cells_on_y_axis = 0
///empty spatial grid cell content lists are just a reference to this instead of a standalone list to save memory without needed to check if its null when iterating
var/list/dummy_list = list()
///list of all of /mob/abstract/oranges_ear instances we have pregenerated for view() iteration speedup
var/list/mob/abstract/oranges_ear/pregenerated_oranges_ears = list()
///how many pregenerated /mob/abstract/oranges_ear instances currently exist. this should hopefully never exceed its starting value
var/number_of_oranges_ears = NUMBER_OF_PREGENERATED_ORANGES_EARS
/datum/controller/subsystem/spatial_grid/New()
NEW_SS_GLOBAL(SSspatial_grid)
/datum/controller/subsystem/spatial_grid/Initialize(start_timeofday)
. = ..()
cells_on_x_axis = SPATIAL_GRID_CELLS_PER_SIDE(world.maxx)
cells_on_y_axis = SPATIAL_GRID_CELLS_PER_SIDE(world.maxy)
for(var/z_level in 1 to world.maxz)
propogate_spatial_grid_to_new_z(null, z_level)
CHECK_TICK
//go through the pre init queue for anything waiting to be let in the grid
for(var/channel_type in waiting_to_add_by_type)
for(var/atom/movable/movable as anything in waiting_to_add_by_type[channel_type])
var/turf/movable_turf = get_turf(movable)
if(movable_turf)
enter_cell(movable, movable_turf)
UnregisterSignal(movable, COMSIG_PARENT_PREQDELETED)
waiting_to_add_by_type[channel_type] -= movable
pregenerate_more_oranges_ears(NUMBER_OF_PREGENERATED_ORANGES_EARS)
RegisterSignal(SSdcs, COMSIG_GLOB_NEW_Z, .proc/propogate_spatial_grid_to_new_z)
RegisterSignal(SSdcs, COMSIG_GLOB_EXPANDED_WORLD_BOUNDS, .proc/after_world_bounds_expanded)
///add a movable to the pre init queue for whichever type is specified so that when the subsystem initializes they get added to the grid
/datum/controller/subsystem/spatial_grid/proc/enter_pre_init_queue(atom/movable/waiting_movable, type)
RegisterSignal(waiting_movable, COMSIG_PARENT_PREQDELETED, .proc/queued_item_deleted, override = TRUE)
//override because something can enter the queue for two different types but that is done through unrelated procs that shouldnt know about eachother
waiting_to_add_by_type[type] += waiting_movable
///removes an initialized and probably deleted movable from our pre init queue before we're initialized
/datum/controller/subsystem/spatial_grid/proc/remove_from_pre_init_queue(atom/movable/movable_to_remove, exclusive_type)
if(exclusive_type)
waiting_to_add_by_type[exclusive_type] -= movable_to_remove
var/waiting_movable_is_in_other_queues = FALSE//we need to check if this movable is inside the other queues
for(var/type in waiting_to_add_by_type)
if(movable_to_remove in waiting_to_add_by_type[type])
waiting_movable_is_in_other_queues = TRUE
if(!waiting_movable_is_in_other_queues)
UnregisterSignal(movable_to_remove, COMSIG_PARENT_PREQDELETED)
return
UnregisterSignal(movable_to_remove, COMSIG_PARENT_PREQDELETED)
for(var/type in waiting_to_add_by_type)
waiting_to_add_by_type[type] -= movable_to_remove
///if a movable is inside our pre init queue before we're initialized and it gets deleted we need to remove that reference with this proc
/datum/controller/subsystem/spatial_grid/proc/queued_item_deleted(atom/movable/movable_being_deleted)
SIGNAL_HANDLER
remove_from_pre_init_queue(movable_being_deleted, null)
///creates the spatial grid for a new z level
/datum/controller/subsystem/spatial_grid/proc/propogate_spatial_grid_to_new_z(datum/controller/subsystem/processing/dcs/fucking_dcs, var/z_level)
SIGNAL_HANDLER
var/list/new_cell_grid = list()
grids_by_z_level += list(new_cell_grid)
for(var/y in 1 to cells_on_y_axis)
new_cell_grid += list(list())
for(var/x in 1 to cells_on_x_axis)
var/datum/spatial_grid_cell/cell = new(x, y, z_level)
new_cell_grid[y] += cell
///creates number_to_generate new oranges_ear's and adds them to the subsystems list of ears.
///i really fucking hope this never gets called after init :clueless:
/datum/controller/subsystem/spatial_grid/proc/pregenerate_more_oranges_ears(number_to_generate)
for(var/new_ear in 1 to number_to_generate)
pregenerated_oranges_ears += new/mob/abstract/oranges_ear(null)
number_of_oranges_ears = length(pregenerated_oranges_ears)
///allocate one [/mob/abstract/oranges_ear] mob per turf containing atoms_that_need_ears and give them a reference to every listed atom in their turf.
///if an oranges_ear is allocated to a turf that already has an oranges_ear then the second one fails to allocate (and gives the existing one the atom it was assigned to)
/datum/controller/subsystem/spatial_grid/proc/assign_oranges_ears(list/atoms_that_need_ears)
var/input_length = length(atoms_that_need_ears)
if(input_length > number_of_oranges_ears)
crash_with("somehow, for some reason, more than the preset generated number of oranges ears was requested. thats fucking [number_of_oranges_ears]. this is not good that should literally never happen")
pregenerate_more_oranges_ears(input_length - number_of_oranges_ears)//im still gonna DO IT but ill complain about it
. = list()
///the next unallocated /mob/abstract/oranges_ear that we try to allocate to assigned_atom's turf
var/mob/abstract/oranges_ear/current_ear
///the next atom in atoms_that_need_ears an ear assigned to it
var/atom/assigned_atom
///the turf loc of the current assigned_atom. turfs are used to track oranges_ears already assigned to one location so we dont allocate more than one
///because allocating more than one oranges_ear to a given loc wastes view iterations
var/turf/turf_loc
for(var/current_ear_index in 1 to input_length)
assigned_atom = atoms_that_need_ears[current_ear_index]
turf_loc = get_turf(assigned_atom)
if(!turf_loc)
continue
current_ear = pregenerated_oranges_ears[current_ear_index]
if(turf_loc.assigned_oranges_ear)
turf_loc.assigned_oranges_ear.references += assigned_atom
continue //if theres already an oranges_ear mob at assigned_movable's turf we give assigned_movable to it instead and dont allocate ourselves
current_ear.references += assigned_atom
current_ear.loc = turf_loc //normally this is bad, but since this is meant to be as fast as possible we literally just need to exist there for view() to see us
turf_loc.assigned_oranges_ear = current_ear
. += current_ear
///adds cells to the grid for every z level when world.maxx or world.maxy is expanded after this subsystem is initialized. hopefully this is never needed.
///because i never tested this.
/datum/controller/subsystem/spatial_grid/proc/after_world_bounds_expanded(datum/controller/subsystem/processing/dcs/fucking_dcs, has_expanded_world_maxx, has_expanded_world_maxy)
SIGNAL_HANDLER
var/old_x_axis = cells_on_x_axis
var/old_y_axis = cells_on_y_axis
cells_on_x_axis = SPATIAL_GRID_CELLS_PER_SIDE(world.maxx)
cells_on_y_axis = SPATIAL_GRID_CELLS_PER_SIDE(world.maxy)
for(var/z_level in 1 to length(grids_by_z_level))
var/list/z_level_gridmap = grids_by_z_level[z_level]
for(var/cell_row_for_expanded_y_axis in 1 to cells_on_y_axis)
if(cell_row_for_expanded_y_axis > old_y_axis)//we are past the old length of the number of rows, so add to the list
z_level_gridmap += list(list())
//now we know theres a row at this position, so add cells to it that need to be added and update the ones that already exist
var/list/cell_row = z_level_gridmap[cell_row_for_expanded_y_axis]
for(var/grid_cell_for_expanded_x_axis in 1 to cells_on_x_axis)
if(grid_cell_for_expanded_x_axis > old_x_axis)
var/datum/spatial_grid_cell/new_cell_inserted = new(grid_cell_for_expanded_x_axis, cell_row_for_expanded_y_axis, z_level)
cell_row += new_cell_inserted
continue
//now we know the cell index we're at contains an already existing cell that needs its x and y values updated
var/datum/spatial_grid_cell/old_cell_that_needs_updating = cell_row[grid_cell_for_expanded_x_axis]
old_cell_that_needs_updating.cell_x = grid_cell_for_expanded_x_axis
old_cell_that_needs_updating.cell_y = cell_row_for_expanded_y_axis
///the left or bottom side index of a box composed of spatial grid cells with the given actual center x or y coordinate
#define BOUNDING_BOX_MIN(center_coord) max(ROUND_UP((center_coord - range) / SPATIAL_GRID_CELLSIZE), 1)
///the right or upper side index of a box composed of spatial grid cells with the given center x or y coordinate.
///outputted value cant exceed the number of cells on that axis
#define BOUNDING_BOX_MAX(center_coord, axis_size) min(ROUND_UP((center_coord + range) / SPATIAL_GRID_CELLSIZE), axis_size)
/**
* https://en.wikipedia.org/wiki/Range_searching#Orthogonal_range_searching
*
* searches through the grid cells intersecting a rectangular search space (with sides of length 2 * range) then returns all contents of type inside them.
* much faster than iterating through view() to find all of what you want.
*
* this does NOT return things only in range distance from center! the search space is a square not a circle, if you want only things in a certain distance
* then you need to filter that yourself
*
* * center - the atom that is the center of the searched circle
* * type - the type of grid contents you are looking for, see __DEFINES/spatial_grid.dm
* * range - the bigger this is, the more spatial grid cells the search space intersects
*/
/datum/controller/subsystem/spatial_grid/proc/orthogonal_range_search(atom/center, type, range)
var/turf/center_turf = get_turf(center)
var/center_x = center_turf.x//used inside the macros
var/center_y = center_turf.y
. = list()
//cache for sanic speeds
var/cells_on_y_axis = src.cells_on_y_axis
var/cells_on_x_axis = src.cells_on_x_axis
if(grids_by_z_level.len < center_turf.z)
return
//technically THIS list only contains lists, but inside those lists are grid cell datums and we can go without a SINGLE var init if we do this
var/list/datum/spatial_grid_cell/grid_level = grids_by_z_level[center_turf.z]
switch(type)
if(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS)
for(var/row in BOUNDING_BOX_MIN(center_y) to BOUNDING_BOX_MAX(center_y, cells_on_y_axis))
for(var/x_index in BOUNDING_BOX_MIN(center_x) to BOUNDING_BOX_MAX(center_x, cells_on_x_axis))
. += grid_level[row][x_index].client_contents
if(SPATIAL_GRID_CONTENTS_TYPE_HEARING)
for(var/row in BOUNDING_BOX_MIN(center_y) to BOUNDING_BOX_MAX(center_y, cells_on_y_axis))
for(var/x_index in BOUNDING_BOX_MIN(center_x) to BOUNDING_BOX_MAX(center_x, cells_on_x_axis))
. += grid_level[row][x_index].hearing_contents
return .
///get the grid cell encomapassing targets coordinates
/datum/controller/subsystem/spatial_grid/proc/get_cell_of(atom/target)
var/turf/target_turf = get_turf(target)
if(!target_turf)
return
return grids_by_z_level[target_turf.z][ROUND_UP(target_turf.y / SPATIAL_GRID_CELLSIZE)][ROUND_UP(target_turf.x / SPATIAL_GRID_CELLSIZE)]
///get all grid cells intersecting the bounding box around center with sides of length 2 * range
/datum/controller/subsystem/spatial_grid/proc/get_cells_in_range(atom/center, range)
var/turf/center_turf = get_turf(center)
var/center_x = center_turf.x
var/center_y = center_turf.y
var/list/intersecting_grid_cells = list()
//the minimum x and y cell indexes to test
var/min_x = max(ROUND_UP((center_x - range) / SPATIAL_GRID_CELLSIZE), 1)
var/min_y = max(ROUND_UP((center_y - range) / SPATIAL_GRID_CELLSIZE), 1)//calculating these indices only takes around 2 microseconds
//the maximum x and y cell indexes to test
var/max_x = min(ROUND_UP((center_x + range) / SPATIAL_GRID_CELLSIZE), cells_on_x_axis)
var/max_y = min(ROUND_UP((center_y + range) / SPATIAL_GRID_CELLSIZE), cells_on_y_axis)
var/list/grid_level = grids_by_z_level[center_turf.z]
for(var/row in min_y to max_y)
var/list/grid_row = grid_level[row]
for(var/x_index in min_x to max_x)
intersecting_grid_cells += grid_row[x_index]
return intersecting_grid_cells
///find the spatial map cell that target belongs to, then add target's important_recusive_contents to it.
///make sure to provide the turf new_target is "in"
/datum/controller/subsystem/spatial_grid/proc/enter_cell(atom/movable/new_target, turf/target_turf)
if(init_state != SS_INITSTATE_DONE)
return
if(QDELETED(new_target))
CRASH("qdeleted or null target trying to enter the spatial grid!")
if(!target_turf || !new_target?.important_recursive_contents)
CRASH("/datum/controller/subsystem/spatial_grid/proc/enter_cell() was given null arguments or a new_target without important_recursive_contents!")
var/x_index = ROUND_UP(target_turf.x / SPATIAL_GRID_CELLSIZE)
var/y_index = ROUND_UP(target_turf.y / SPATIAL_GRID_CELLSIZE)
var/z_index = target_turf.z
var/datum/spatial_grid_cell/intersecting_cell = grids_by_z_level[z_index][y_index][x_index]
if(new_target.important_recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS])
GRID_CELL_SET(intersecting_cell.client_contents, new_target.important_recursive_contents[SPATIAL_GRID_CONTENTS_TYPE_CLIENTS])
SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(RECURSIVE_CONTENTS_CLIENT_MOBS), new_target)
if(new_target.important_recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE])
GRID_CELL_SET(intersecting_cell.hearing_contents, new_target.important_recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE])
SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_ENTERED(RECURSIVE_CONTENTS_HEARING_SENSITIVE), new_target)
/**
* find the spatial map cell that target used to belong to, then subtract target's important_recusive_contents from it.
* make sure to provide the turf old_target used to be "in"
*
* * old_target - the thing we want to remove from the spatial grid cell
* * target_turf - the turf we use to determine the cell we're removing from
* * exclusive_type - either null or a valid contents channel. if you just want to remove a single type from the grid cell then use this
*/
/datum/controller/subsystem/spatial_grid/proc/exit_cell(atom/movable/old_target, turf/target_turf, exclusive_type)
if(init_state != SS_INITSTATE_DONE)
return
if(QDELETED(old_target))
CRASH("qdeleted or null target trying to enter the spatial grid")
if(!target_turf || !old_target?.important_recursive_contents)
CRASH("/datum/controller/subsystem/spatial_grid/proc/exit_cell() was given null arguments or a new_target without important_recursive_contents!")
var/x_index = ROUND_UP(target_turf.x / SPATIAL_GRID_CELLSIZE)
var/y_index = ROUND_UP(target_turf.y / SPATIAL_GRID_CELLSIZE)
var/z_index = target_turf.z
var/list/grid = grids_by_z_level[z_index]
var/datum/spatial_grid_cell/intersecting_cell = grid[y_index][x_index]
if(exclusive_type && old_target.important_recursive_contents[exclusive_type])
switch(exclusive_type)
if(RECURSIVE_CONTENTS_CLIENT_MOBS)
GRID_CELL_REMOVE(intersecting_cell.client_contents, old_target.important_recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS])
if(RECURSIVE_CONTENTS_HEARING_SENSITIVE)
GRID_CELL_REMOVE(intersecting_cell.hearing_contents, old_target.important_recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE])
SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(exclusive_type), old_target)
return
if(old_target.important_recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS])
GRID_CELL_REMOVE(intersecting_cell.client_contents, old_target.important_recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS])
SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), old_target)
if(old_target.important_recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE])
GRID_CELL_REMOVE(intersecting_cell.hearing_contents, old_target.important_recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE])
SEND_SIGNAL(intersecting_cell, SPATIAL_GRID_CELL_EXITED(RECURSIVE_CONTENTS_HEARING_SENSITIVE), old_target)
///find the cell this movable is associated with and removes it from all lists
/datum/controller/subsystem/spatial_grid/proc/force_remove_from_cell(atom/movable/to_remove, datum/spatial_grid_cell/input_cell)
if(init_state != SS_INITSTATE_DONE)
remove_from_pre_init_queue(to_remove)//the spatial grid doesnt exist yet, so just take it out of the queue
return
if(!input_cell)
input_cell = get_cell_of(to_remove)
if(!input_cell)
find_hanging_cell_refs_for_movable(to_remove, TRUE)
return
GRID_CELL_REMOVE(input_cell.client_contents, to_remove)
GRID_CELL_REMOVE(input_cell.hearing_contents, to_remove)
///if shit goes south, this will find hanging references for qdeleting movables inside the spatial grid
/datum/controller/subsystem/spatial_grid/proc/find_hanging_cell_refs_for_movable(atom/movable/to_remove, remove_from_cells = TRUE)
var/list/queues_containing_movable = list()
for(var/queue_channel in waiting_to_add_by_type)
var/list/queue_list = waiting_to_add_by_type[queue_channel]
if(to_remove in queue_list)
queues_containing_movable += queue_channel//just add the associative key
if(remove_from_cells)
queue_list -= to_remove
if(init_state != SS_INITSTATE_DONE)
return queues_containing_movable
var/list/containing_cells = list()
for(var/list/z_level_grid as anything in grids_by_z_level)
for(var/list/cell_row as anything in z_level_grid)
for(var/datum/spatial_grid_cell/cell as anything in cell_row)
if(to_remove in (cell.hearing_contents | cell.client_contents))
containing_cells += cell
if(remove_from_cells)
force_remove_from_cell(to_remove, cell)
return containing_cells
///debug proc for checking if a movable is in multiple cells when it shouldnt be (ie always unless multitile entering is implemented)
/atom/proc/find_all_cells_containing(remove_from_cells = FALSE)
var/datum/spatial_grid_cell/real_cell = SSspatial_grid.get_cell_of(src)
var/list/containing_cells = SSspatial_grid.find_hanging_cell_refs_for_movable(src, FALSE, remove_from_cells)
message_admins("[src] is located in the contents of [length(containing_cells)] spatial grid cells")
var/cell_coords = "the following cells contain [src]: "
for(var/datum/spatial_grid_cell/cell as anything in containing_cells)
cell_coords += "([cell.cell_x], [cell.cell_y], [cell.cell_z]), "
message_admins(cell_coords)
message_admins("[src] is supposed to only be contained in the cell at indexes ([real_cell.cell_x], [real_cell.cell_y], [real_cell.cell_z]). but is contained at the cells at [cell_coords]")
///debug proc for finding how full the cells of src's z level are
/atom/proc/find_grid_statistics_for_z_level(insert_clients = 0)
var/raw_clients = 0
var/raw_hearables = 0
var/cells_with_clients = 0
var/cells_with_hearables = 0
var/list/client_list = list()
var/list/hearable_list = list()
var/total_cells = (world.maxx / SPATIAL_GRID_CELLSIZE) ** 2
var/average_clients_per_cell = 0
var/average_hearables_per_cell = 0
var/hearable_min_x = (world.maxx / SPATIAL_GRID_CELLSIZE)
var/hearable_max_x = 1
var/hearable_min_y = (world.maxy / SPATIAL_GRID_CELLSIZE)
var/hearable_max_y = 1
var/client_min_x = (world.maxx / SPATIAL_GRID_CELLSIZE)
var/client_max_x = 1
var/client_min_y = (world.maxy / SPATIAL_GRID_CELLSIZE)
var/client_max_y = 1
var/list/inserted_clients = list()
if(insert_clients)
var/list/turfs
if(isStationLevel(z))
turfs = station_turfs
else
turfs = block(locate(1,1,z), locate(world.maxx, world.maxy, z))
for(var/client_to_insert in 0 to insert_clients)
var/turf/random_turf = pick(turfs)
var/mob/fake_client = new()
fake_client.important_recursive_contents = list(SPATIAL_GRID_CONTENTS_TYPE_HEARING = list(fake_client), SPATIAL_GRID_CONTENTS_TYPE_CLIENTS = list(fake_client))
fake_client.forceMove(random_turf)
inserted_clients += fake_client
var/list/all_z_level_cells = SSspatial_grid.get_cells_in_range(src, 1000)
for(var/datum/spatial_grid_cell/cell as anything in all_z_level_cells)
var/client_length = length(cell.client_contents)
var/hearable_length = length(cell.hearing_contents)
raw_clients += client_length
raw_hearables += hearable_length
if(client_length)
cells_with_clients++
client_list += cell.client_contents
if(cell.cell_x < client_min_x)
client_min_x = cell.cell_x
if(cell.cell_x > client_max_x)
client_max_x = cell.cell_x
if(cell.cell_y < client_min_y)
client_min_y = cell.cell_y
if(cell.cell_y > client_max_y)
client_max_y = cell.cell_y
if(hearable_length)
cells_with_hearables++
hearable_list += cell.hearing_contents
if(cell.cell_x < hearable_min_x)
hearable_min_x = cell.cell_x
if(cell.cell_x > hearable_max_x)
hearable_max_x = cell.cell_x
if(cell.cell_y < hearable_min_y)
hearable_min_y = cell.cell_y
if(cell.cell_y > hearable_max_y)
hearable_max_y = cell.cell_y
var/total_client_distance = 0
var/total_hearable_distance = 0
var/average_client_distance = 0
var/average_hearable_distance = 0
for(var/hearable in hearable_list)//n^2 btw
for(var/other_hearable in hearable_list)
if(hearable == other_hearable)
continue
total_hearable_distance += get_dist(hearable, other_hearable)
for(var/client in client_list)//n^2 btw
for(var/other_client in client_list)
if(client == other_client)
continue
total_client_distance += get_dist(client, other_client)
if(length(hearable_list))
average_hearable_distance = total_hearable_distance / length(hearable_list)
if(length(client_list))
average_client_distance = total_client_distance / length(client_list)
average_clients_per_cell = raw_clients / total_cells
average_hearables_per_cell = raw_hearables / total_cells
for(var/mob/inserted_client as anything in inserted_clients)
qdel(inserted_client)
message_admins("on z level [z] there are [raw_clients] clients ([insert_clients] of whom are fakes inserted to random station turfs) \
and [raw_hearables] hearables. all of whom are inside the bounding box given by \
clients: ([client_min_x], [client_min_y]) x ([client_max_x], [client_max_y]) \
and hearables: ([hearable_min_x], [hearable_min_y]) x ([hearable_max_x], [hearable_max_y]) \
on average there are [average_clients_per_cell] clients per cell and [average_hearables_per_cell] hearables per cell. \
[cells_with_clients] cells have clients and [cells_with_hearables] have hearables, \
the average client distance is: [average_client_distance] and the average hearable_distance is [average_hearable_distance].")
#undef GRID_CELL_ADD
#undef GRID_CELL_REMOVE
#undef GRID_CELL_SET

View File

@@ -116,7 +116,7 @@
var/pda_msg_amt = 0
var/rc_msg_amt = 0
for(var/obj/machinery/message_server/MS in SSmachinery.machinery)
for(var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms)
if(MS.pda_msgs.len > pda_msg_amt)
pda_msg_amt = MS.pda_msgs.len
if(MS.rc_msgs.len > rc_msg_amt)

View File

@@ -4,6 +4,8 @@
var/tmp/isprocessing = 0
var/tmp/gcDestroyed //Time when this object was destroyed.
/// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...))
var/list/status_traits
/// Components attached to this datum
/// Lazy associated list in the structure of `type:component/list of components`
var/list/datum_components

View File

@@ -53,8 +53,8 @@
if(T.z != AB.z || get_dist(adestination, AB) > 8 || (AB.stat & (NOPOWER | BROKEN)))
continue
AB.use_power_oneoff(AB.active_power_usage)
bad_turfs += circlerangeturfs(get_turf(AB),8)
good_turfs += circlerangeturfs(get_turf(AB),9)
bad_turfs += circle_range_turfs(get_turf(AB),8)
good_turfs += circle_range_turfs(get_turf(AB),9)
if(length(good_turfs) && length(bad_turfs))
good_turfs -= bad_turfs
if(length(good_turfs))
@@ -138,7 +138,7 @@
var/turf/curturf = get_turf(teleatom)
var/area/destarea = get_area(destination)
if(precision)
var/list/posturfs = circlerangeturfs(destination,precision)
var/list/posturfs = circle_range_turfs(destination,precision)
destturf = LAZYPICK(posturfs, null)
else
destturf = get_turf(destination)

View File

@@ -42,7 +42,7 @@
if (!devices_line)
devices_line = new
devices[filter] = devices_line
devices_line+=device
devices_line |= device
// var/list/obj/devices_line___ = devices[filter_str]
// var/l = devices_line___.len
//log_debug("DEBUG: devices_line.len=[devices_line.len]")

View File

@@ -30,61 +30,3 @@
/datum/signal/Destroy()
..()
return QDEL_HINT_IWILLGC
/datum/signal/proc/tcombroadcast(var/message, var/freq, var/source, var/job, var/verb, var/language)
var/datum/signal/newsign = new
var/obj/machinery/telecomms/server/S = data["server"]
var/obj/item/device/radio/hradio = S.server_radio
if(!hradio)
error("[src] has no radio.")
return
if((!message || message == "") && message != 0)
message = "*beep*"
if(!source)
source = "[html_encode(uppertext(S.id))]"
hradio = new // sets the hradio as a radio intercom
if(!freq)
freq = PUB_FREQ
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place
if(!job)
job = "?"
if(!language || language == "")
language = LANGUAGE_TCB
var/datum/language/L = all_languages[language]
if(!L || !(L.flags & TCOMSSIM))
L = all_languages[LANGUAGE_TCB]
newsign.data["mob"] = null
newsign.data["mobtype"] = /mob/living/carbon/human
newsign.data["name"] = source
newsign.data["realname"] = newsign.data["name"]
newsign.data["job"] = job
newsign.data["compression"] = 0
newsign.data["message"] = message
newsign.data["language"] = L
newsign.data["type"] = 2 // artificial broadcast
if(!isnum(freq))
freq = text2num(freq)
newsign.frequency = freq
var/datum/radio_frequency/connection = SSradio.return_frequency(freq)
newsign.data["connection"] = connection
newsign.data["radio"] = hradio
newsign.data["vmessage"] = message
newsign.data["vname"] = source
newsign.data["vmask"] = 0
newsign.data["level"] = list()
newsign.data["verb"] = verb
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
if(!pass)
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters

View File

@@ -16,26 +16,26 @@ var/const/WIRE_TRANSMIT = 4
var/obj/item/device/radio/R = holder
switch(index)
if(WIRE_SIGNAL)
R.listening = !R.listening && !IsIndexCut(WIRE_RECEIVE)
R.broadcasting = R.listening && !IsIndexCut(WIRE_TRANSMIT)
R.set_listening(!R.get_listening() && !IsIndexCut(WIRE_RECEIVE))
R.set_broadcasting(R.get_listening() && !IsIndexCut(WIRE_TRANSMIT))
if(WIRE_RECEIVE)
R.listening = !R.listening && !IsIndexCut(WIRE_SIGNAL)
R.set_listening(!R.get_listening() && !IsIndexCut(WIRE_SIGNAL))
if(WIRE_TRANSMIT)
R.broadcasting = !R.broadcasting && !IsIndexCut(WIRE_SIGNAL)
R.set_broadcasting(!R.get_broadcasting() && !IsIndexCut(WIRE_SIGNAL))
SSnanoui.update_uis(holder)
/datum/wires/radio/UpdateCut(var/index, var/mended)
var/obj/item/device/radio/R = holder
switch(index)
if(WIRE_SIGNAL)
R.listening = mended && !IsIndexCut(WIRE_RECEIVE)
R.broadcasting = mended && !IsIndexCut(WIRE_TRANSMIT)
R.set_listening(mended && !IsIndexCut(WIRE_RECEIVE))
R.set_broadcasting(mended && !IsIndexCut(WIRE_TRANSMIT))
if(WIRE_RECEIVE)
R.listening = mended && !IsIndexCut(WIRE_SIGNAL)
R.set_listening(mended && !IsIndexCut(WIRE_SIGNAL))
if(WIRE_TRANSMIT)
R.broadcasting = mended && !IsIndexCut(WIRE_SIGNAL)
R.set_broadcasting(mended && !IsIndexCut(WIRE_SIGNAL))
SSnanoui.update_uis(holder)

View File

@@ -1,8 +1,3 @@
#define TELECOMMS_RECEPTION_NONE 1
#define TELECOMMS_RECEPTION_SENDER 2
#define TELECOMMS_RECEPTION_RECEIVER 4
#define TELECOMMS_RECEPTION_BOTH 8
/proc/register_radio(source, old_frequency, new_frequency, radio_filter)
if(old_frequency)
SSradio.remove_object(source, old_frequency)
@@ -34,48 +29,3 @@
freq_text = format_frequency(display_freq)
return freq_text
/datum/reception
var/obj/machinery/message_server/message_server = null
var/telecomms_reception = TELECOMMS_RECEPTION_NONE
var/message = ""
/datum/receptions
var/obj/machinery/message_server/message_server = null
var/sender_reception = TELECOMMS_RECEPTION_NONE
var/list/receiver_reception = new
/proc/get_message_server()
if(message_servers)
for (var/obj/machinery/message_server/MS in message_servers)
if(MS.active && !within_jamming_range(MS))
return MS
return null
/proc/check_signal(var/datum/signal/signal)
return signal && signal.data["done"]
/proc/get_sender_reception(var/atom/sender, var/datum/signal/signal)
if (check_signal(signal) && !within_jamming_range(sender))
return TELECOMMS_RECEPTION_SENDER
return TELECOMMS_RECEPTION_NONE
/proc/get_receiver_reception(var/receiver, var/datum/signal/signal)
if(receiver && check_signal(signal) && !within_jamming_range(receiver))
var/turf/pos = get_turf(receiver)
if(pos && (pos.z in signal.data["level"]))
return TELECOMMS_RECEPTION_RECEIVER
return TELECOMMS_RECEPTION_NONE
/proc/get_reception(var/atom/sender, var/receiver, var/message = "", var/do_sleep = 1)
var/datum/reception/reception = new
// check if telecomms I/O route 1459 is stable
reception.message_server = get_message_server()
var/datum/signal/signal = sender.telecomms_process(do_sleep) // Be aware that this proc calls sleep, to simulate transmition delays
reception.telecomms_reception |= get_sender_reception(sender, signal)
reception.telecomms_reception |= get_receiver_reception(receiver, signal)
reception.message = signal && signal.data["compression"] > 0 ? Gibberish(message, signal.data["compression"] + 50) : message
return reception

View File

@@ -50,12 +50,36 @@
else
return null
// Will return the contents of an atom recursively to a depth of "searchDepth".
/atom/proc/GetAllContents(searchDepth = 5, checkClient = 1, checkSight = 1, includeMobs = 1, includeObjects = 1)
var/list/L = list()
recursive_content_check(src, L, searchDepth, checkClient, checkSight, includeMobs, includeObjects)
// Returns src and all recursive contents in a list.
/atom/proc/GetAllContents()
. = list(src)
var/i = 0
while(i < length(.))
var/atom/A = .[++i]
. += A.contents
return L
// identical to GetAllContents but returns a list of atoms of the type passed in the argument
/atom/proc/get_all_contents_of_type(type)
var/list/processing_list = list(src)
. = list()
while(length(processing_list))
var/atom/A = processing_list[1]
processing_list.Cut(1, 2)
processing_list += A.contents
if(istype(A, type))
. += A
// Returns a list of all locations (except the area) the movable is within
/proc/get_nested_locs(atom/movable/atom_on_location, include_turf = FALSE)
. = list()
var/atom/location = atom_on_location.loc
var/turf/our_turf = get_turf(atom_on_location)
while (location && location != our_turf)
. += location
location = location.loc
if(our_turf && include_turf)
. += our_turf
// Return flags that should be added to the viewer's sight variable.
// Otherwise return a negative number to indicate that the view should be cancelled.
@@ -554,24 +578,18 @@
// "blind_message" (optional) is what blind people will hear e.g. "You hear something!"
/atom/proc/visible_message(var/message, var/blind_message, var/range = world.view, var/intent_message = null, var/intent_range = 7)
set waitfor = FALSE
var/turf/T = get_turf(src)
var/list/mobs = list()
var/list/objs = list()
get_mobs_or_objs_in_view(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW)
var/list/hearers = get_hearers_in_view(range, src)
for(var/o in objs)
var/obj/O = o
O.show_message(message,1,blind_message,2)
for(var/m in mobs)
var/mob/M = m
if(M.see_invisible >= invisibility)
M.show_message(message,1,blind_message,2)
else if(blind_message)
for(var/atom/movable/AM as anything in hearers)
if(ismob(AM))
var/mob/M = AM
if(M.see_invisible < invisibility)
M.show_message(blind_message, 2)
continue
AM.show_message(message, 1, blind_message, 2)
if(intent_message)
intent_message(intent_message, intent_range, mobs) // pass our mobs list through to intent_message so it doesn't have to call get_mobs_or_objs_in_view again
intent_message(intent_message, intent_range, hearers) // pass our hearers list through to intent_message so it doesn't have to call get_hearers again
// Show a message to all mobs and objects in earshot of this atom.
// Use for objects performing audible actions.
@@ -580,32 +598,25 @@
// "hearing_distance" (optional) is the range, how many tiles away the message can be heard.
/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance, var/intent_message = null, var/intent_range = 7)
set waitfor = FALSE
var/range = world.view
if(hearing_distance)
range = hearing_distance
var/turf/T = get_turf(src)
var/list/mobs = list()
var/list/objs = list()
get_mobs_or_objs_in_view(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW)
for(var/m in mobs)
var/mob/M = m
M.show_message(message,2,deaf_message,1)
for(var/o in objs)
var/obj/O = o
O.show_message(message,2,deaf_message,1)
if(!hearing_distance)
hearing_distance = world.view
var/list/hearers = get_hearers_in_view(hearing_distance, src)
for(var/atom/movable/AM as anything in hearers)
AM.show_message(message, 2, deaf_message, 1)
if(intent_message)
intent_message(intent_message, intent_range, mobs) // pass our mobs list through to intent_message so it doesn't have to call get_mobs_or_objs_in_view again
intent_message(intent_message, intent_range, hearers) // pass our hearers list through to intent_message so it doesn't have to call get_hearers again
/atom/proc/intent_message(var/message, var/range = 7, var/list/mobs = list())
/atom/proc/intent_message(var/message, var/range = 7, var/list/hearers = list())
set waitfor = FALSE
if(air_sound(src))
var/turf/T = get_turf(src)
if(!mobs.len)
get_mobs_or_objs_in_view(T, range, mobs, checkghosts = ONLY_GHOSTS_IN_VIEW)
if(!hearers.len)
hearers = get_hearers_in_view(range, src)
for(var/mob/living/carbon/human/H as anything in intent_listener)
if(!(H in mobs))
if(!(H in hearers))
if(src.z == H.z && get_dist(src, H) <= range)
H.intent_listen(src, message)

View File

@@ -27,6 +27,13 @@
var/list/contained_mobs
appearance_flags = DEFAULT_APPEARANCE_FLAGS | TILE_BOUND
/**
* an associative lazylist of relevant nested contents by "channel", the list is of the form: list(channel = list(important nested contents of that type))
* each channel has a specific purpose and is meant to replace potentially expensive nested contents iteration
* do NOT add channels to this for little reason as it can add considerable memory usage.
*/
var/list/important_recursive_contents
// We don't really need this, and apparently defining it slows down GC.
/*/atom/movable/Del()
if(!QDELING(src) && loc)
@@ -35,7 +42,13 @@
..()*/
/atom/movable/Destroy()
if (important_recursive_contents && (important_recursive_contents[RECURSIVE_CONTENTS_CLIENT_MOBS] || important_recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE]))
SSspatial_grid.force_remove_from_cell(src)
LAZYCLEARLIST(contained_mobs)
LAZYCLEARLIST(important_recursive_contents)
. = ..()
for(var/atom/movable/AM in contents)
qdel(AM)
loc = null
@@ -313,14 +326,15 @@
// Core movement hooks & procs.
/atom/movable/proc/forceMove(atom/destination)
if(destination)
if(!destination)
return FALSE
if(loc)
loc.Exited(src, destination)
var/old_loc = loc
loc = destination
loc.Entered(src, old_loc)
return 1
return 0
Moved(old_loc, TRUE)
return TRUE
/atom/movable/Move()
var/old_loc = loc
@@ -345,6 +359,132 @@
if (bound_overlay.dir != dir)
bound_overlay.set_dir(dir)
Moved(old_loc, FALSE)
/atom/movable/proc/Moved(atom/old_loc, forced)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, forced)
update_grid_location(old_loc, src)
/atom/movable/proc/update_grid_location(atom/old_loc)
if(!HAS_SPATIAL_GRID_CONTENTS(src))
return
var/turf/old_turf = get_turf(old_loc)
var/turf/new_turf = get_turf(src)
if(old_turf && new_turf && (old_turf.z != new_turf.z \
|| ROUND_UP(old_turf.x / SPATIAL_GRID_CELLSIZE) != ROUND_UP(new_turf.x / SPATIAL_GRID_CELLSIZE) \
|| ROUND_UP(old_turf.y / SPATIAL_GRID_CELLSIZE) != ROUND_UP(new_turf.y / SPATIAL_GRID_CELLSIZE)))
SSspatial_grid.exit_cell(src, old_turf)
SSspatial_grid.enter_cell(src, new_turf)
else if(old_turf && !new_turf)
SSspatial_grid.exit_cell(src, old_turf)
else if(new_turf && !old_turf)
SSspatial_grid.enter_cell(src, new_turf)
/atom/movable/Exited(atom/movable/gone, direction)
. = ..()
if (LAZYLEN(gone.important_recursive_contents))
var/list/nested_locs = get_nested_locs(src) + src
for (var/channel in gone.important_recursive_contents)
for (var/atom/movable/location as anything in nested_locs)
LAZYREMOVEASSOC(location.important_recursive_contents, channel, gone.important_recursive_contents[channel])
/atom/movable/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
if (LAZYLEN(arrived.important_recursive_contents))
var/list/nested_locs = get_nested_locs(src) + src
for (var/channel in arrived.important_recursive_contents)
for (var/atom/movable/location as anything in nested_locs)
LAZYORASSOCLIST(location.important_recursive_contents, channel, arrived.important_recursive_contents[channel])
//allows this movable to hear and adds itself to the important_recursive_contents list of itself and every movable loc its in
/atom/movable/proc/become_hearing_sensitive(trait_source = TRAIT_GENERIC)
if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
for (var/atom/movable/location as anything in get_nested_locs(src) + src)
LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_HEARING_SENSITIVE, src)
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.init_state == SS_INITSTATE_DONE)
SSspatial_grid.enter_cell(src, our_turf)
else if(our_turf && SSspatial_grid.init_state != SS_INITSTATE_DONE)//SSspatial_grid isnt init'd yet, add ourselves to the queue
SSspatial_grid.enter_pre_init_queue(src, RECURSIVE_CONTENTS_HEARING_SENSITIVE)
ADD_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
/**
* removes the hearing sensitivity channel from the important_recursive_contents list of this and all nested locs containing us if there are no more sources of the trait left
* since RECURSIVE_CONTENTS_HEARING_SENSITIVE is also a spatial grid content type, removes us from the spatial grid if the trait is removed
*
* * trait_source - trait source define or ALL, if ALL, force removes hearing sensitivity. if a trait source define, removes hearing sensitivity only if the trait is removed
*/
/atom/movable/proc/lose_hearing_sensitivity(trait_source = TRAIT_GENERIC)
if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
return
REMOVE_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
if(HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
return
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.init_state == SS_INITSTATE_DONE)
SSspatial_grid.exit_cell(src, our_turf)
else if(our_turf && SSspatial_grid.init_state != SS_INITSTATE_DONE)
SSspatial_grid.remove_from_pre_init_queue(src, RECURSIVE_CONTENTS_HEARING_SENSITIVE)
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_HEARING_SENSITIVE, src)
///allows this movable to know when it has "entered" another area no matter how many movable atoms its stuffed into, uses important_recursive_contents
/atom/movable/proc/become_area_sensitive(trait_source = TRAIT_GENERIC)
if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
for (var/atom/movable/location as anything in get_nested_locs(src) + src)
LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src)
ADD_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source)
///removes the area sensitive channel from the important_recursive_contents list of this and all nested locs containing us if there are no more source of the trait left
/atom/movable/proc/lose_area_sensitivity(trait_source = TRAIT_GENERIC)
if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
return
REMOVE_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source)
if(HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
return
for (var/atom/movable/location as anything in get_nested_locs(src) + src)
LAZYREMOVE(location.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE], src)
///propogates new_client's mob through our nested contents, similar to other important_recursive_contents procs
///main difference is that client contents need to possibly duplicate recursive contents for the clients mob AND its eye
/atom/movable/proc/enable_client_mobs_in_contents(client/new_client)
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.init_state == SS_INITSTATE_DONE)
SSspatial_grid.enter_cell(src, our_turf, RECURSIVE_CONTENTS_CLIENT_MOBS)
else if(our_turf && SSspatial_grid.init_state != SS_INITSTATE_DONE)
SSspatial_grid.enter_pre_init_queue(src, RECURSIVE_CONTENTS_CLIENT_MOBS)
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
LAZYORASSOCLIST(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, new_client.mob)
///Clears the clients channel of this movables important_recursive_contents list and all nested locs
/atom/movable/proc/clear_important_client_contents(client/former_client)
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.init_state == SS_INITSTATE_DONE)
SSspatial_grid.exit_cell(src, our_turf, RECURSIVE_CONTENTS_CLIENT_MOBS)
else if(our_turf && SSspatial_grid.init_state != SS_INITSTATE_DONE)
SSspatial_grid.remove_from_pre_init_queue(src, RECURSIVE_CONTENTS_CLIENT_MOBS)
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
LAZYREMOVEASSOC(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, former_client.mob)
/atom/movable/proc/do_simple_ranged_interaction(var/mob/user)
return FALSE
@@ -431,3 +571,6 @@
/atom/movable/proc/begin_falling(var/lastloc, var/below)
return
/atom/movable/proc/show_message(msg, type, alt, alt_type) //Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
return

View File

@@ -63,8 +63,7 @@
/obj/item/spell/energy_siphon/proc/populate_siphon_list(atom/movable/target)
things_to_siphon.Cut()
things_to_siphon |= target // The recursive check below does not add the object being checked to its list.
things_to_siphon |= recursive_content_check(target, things_to_siphon, recursion_limit = 3, client_check = 0, sight_check = 0, include_mobs = 1, include_objects = 1)
things_to_siphon |= target.GetAllContents()
for(var/atom/movable/AM in things_to_siphon)
if(ishuman(AM)) // We can drain FBPs, so we can skip the test below.
var/mob/living/carbon/human/H = AM

View File

@@ -122,7 +122,7 @@ var/global/list/bluespace_inhibitors
if(temp_apc)
temp_apc.drain_power(0,TRUE,100000)
for(var/atom/movable/AM in circlerange(get_turf(src),20))
for(var/atom/movable/AM in circle_range(get_turf(src),20))
if(AM.anchored)
continue
var/area/A = random_station_area()

View File

@@ -322,7 +322,7 @@
if(isXRay())
see = range(view_range, pos)
else
see = hear(view_range, pos)
see = get_hear(view_range, pos)
return see
/atom/proc/auto_turn()

View File

@@ -2,7 +2,6 @@
/obj/machinery/button/distress
name = "distress beacon launcher"
desc = "Press this button to launch a distress beacon."
var/listening = FALSE
var/recorded_message
/obj/machinery/button/distress/Initialize(mapload, d, populate_components, is_internal)
@@ -16,7 +15,6 @@
attempt_hook_up(my_sector)
/obj/machinery/button/distress/hear_talk(mob/M, text, verb, datum/language/speaking)
if(listening)
recorded_message = text
/obj/machinery/button/distress/attack_hand(var/mob/user)
@@ -35,9 +33,9 @@
return
var/distress_message = input(user, "Enter a distress message that other vessels will receive.", "Distress Beacon")
if(distress_message)
listening = TRUE
become_hearing_sensitive()
user.say(distress_message)
listening = FALSE
lose_hearing_sensitivity()
else
to_chat(user, SPAN_WARNING("The beacon refuses to launch without a message!"))
active = FALSE

View File

@@ -23,6 +23,10 @@ Possible to do for anyone motivated enough:
* Holopad
*/
#define CAN_HEAR_MASTERS (1<<0)
#define CAN_HEAR_ACTIVE_HOLOCALLS (1<<1)
#define CAN_HEAR_ALL_FLAGS (CAN_HEAR_MASTERS|CAN_HEAR_ACTIVE_HOLOCALLS)
#define HOLOPAD_PASSIVE_POWER_USAGE 1
#define HOLOGRAM_POWER_USAGE 2
@@ -58,6 +62,8 @@ Possible to do for anyone motivated enough:
var/list/linked_pdas = list()
var/can_hear_flags = NONE
/obj/machinery/hologram/holopad/Initialize()
. = ..()
@@ -68,7 +74,6 @@ Possible to do for anyone motivated enough:
desc += " Its ID is '[holopad_id]'"
SSmachinery.all_holopads += src
listening_objects += src
light_color = long_range ? rgb(225, 173, 125) : rgb(125, 180, 225)
@@ -173,6 +178,33 @@ Possible to do for anyone motivated enough:
SSvueui.close_user_uis(usr, src)
//setters
/**
* setter for can_hear_flags. handles adding or removing the given flag on can_hear_flags and then adding hearing sensitivity or removing it depending on the final state
* this is necessary because holopads are a significant fraction of the hearable atoms on station which increases the cost of procs that iterate through hearables
* so we need holopads to not be hearable until it is needed
*
* * flag - one of the can_hear_flags flag defines
* * set_flag - boolean, if TRUE sets can_hear_flags to that flag and might add hearing sensitivity if can_hear_flags was NONE before,
* if FALSE unsets the flag and possibly removes hearing sensitivity
*/
/obj/machinery/hologram/holopad/proc/set_can_hear_flags(flag, set_flag = TRUE)
if(!(flag & CAN_HEAR_ALL_FLAGS))
return FALSE //the given flag doesnt exist
if(set_flag)
if(can_hear_flags == NONE)//we couldnt hear before, so become hearing sensitive
become_hearing_sensitive()
can_hear_flags |= flag
return TRUE
else
can_hear_flags &= ~flag
if(can_hear_flags == NONE)
lose_hearing_sensitivity()
return TRUE
/obj/machinery/hologram/holopad/proc/make_call(var/obj/machinery/hologram/holopad/connected_pad, var/mob/user, forced_call)
connected_pad.last_request = world.time
@@ -216,6 +248,7 @@ Possible to do for anyone motivated enough:
connected_pad.clear_holos(FALSE)
connected_pad.connected_pad = null
clear_holos(FALSE)
set_can_hear_flags(CAN_HEAR_ACTIVE_HOLOCALLS, FALSE)
established_connection = FALSE
connected_pad.established_connection = FALSE
connected_pad.update_icon()
@@ -330,9 +363,12 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
/obj/machinery/hologram/holopad/proc/create_holo(mob/M)
var/obj/effect/overlay/hologram/H = new(get_turf(src))
if(isAI(M))
set_can_hear_flags(CAN_HEAR_MASTERS)
if(!isAI(M) && connected_pad)
H.x = src.x - (connected_pad.x - M.x)
H.y = src.y - (connected_pad.y - M.y)
set_can_hear_flags(CAN_HEAR_ACTIVE_HOLOCALLS)
if(!isInSight(H, src))
qdel(H)
return
@@ -359,6 +395,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
for(var/M in active_holograms)
if(!clear_ai && isAI(M))
continue
else if(isAI(M))
set_can_hear_flags(CAN_HEAR_MASTERS, FALSE)
clear_holo(M)
/obj/machinery/hologram/holopad/proc/clear_holo(var/mob/M)
@@ -481,7 +519,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
if(connected_pad)
end_call()
clear_holos(TRUE)
listening_objects -= src
SSmachinery.all_holopads -= src
linked_pdas.Cut()
return ..()
@@ -506,3 +543,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
#undef HOLOPAD_PASSIVE_POWER_USAGE
#undef HOLOGRAM_POWER_USAGE
#undef CAN_HEAR_MASTERS
#undef CAN_HEAR_ACTIVE_HOLOCALLS
#undef CAN_HEAR_ALL_FLAGS

View File

@@ -219,6 +219,16 @@ Class Procs:
/obj/machinery/proc/inoperable(var/additional_flags = 0)
return (stat & (NOPOWER|BROKEN|additional_flags))
/obj/machinery/proc/toggle_power(power_set = -1, additional_flags = 0)
if(power_set >= 0)
update_use_power(power_set)
else if (use_power || inoperable(additional_flags))
update_use_power(POWER_USE_OFF)
else
update_use_power(initial(use_power))
update_icon()
/obj/machinery/CanUseTopic(var/mob/user)
if(stat & BROKEN)
return STATUS_CLOSE

View File

@@ -24,7 +24,7 @@ var/req_console_information = list()
var/list/obj/machinery/requests_console/allConsoles = list()
/obj/machinery/requests_console
name = "Requests Console"
name = "requests console"
desc = "A console intended to send requests to different departments on the station."
icon = 'icons/obj/terminals.dmi'
icon_state = "req_comp"
@@ -118,7 +118,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
announcement.title = "[department] announcement"
announcement.newscast = 1
name = "[department] Requests Console"
name = "[department] requests console"
allConsoles += src
if (departmentType & RC_ASSIST)
req_console_assistance |= department
@@ -240,12 +240,12 @@ var/list/obj/machinery/requests_console/allConsoles = list()
if( href_list["department"] && message )
var/log_msg = message
var/pass = FALSE
screen = RCS_SENTFAIL
for(var/obj/machinery/message_server/MS in SSmachinery.processing)
if(!MS.active)
continue
MS.send_rc_message(ckey(href_list["department"]), department, log_msg, msgStamped, msgVerified, priority)
var/pass = FALSE
var/datum/data_rc_msg/log = new(href_list["department"], department, log_msg, msgStamped, msgVerified, priority)
for (var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms)
if (MS.use_power)
MS.rc_msgs += log
pass = TRUE
if(pass)
screen = RCS_SENTPASS
@@ -414,8 +414,8 @@ var/list/obj/machinery/requests_console/allConsoles = list()
return TRUE
/obj/machinery/requests_console/proc/can_send()
for(var/obj/machinery/message_server/MS in SSmachinery.processing)
if(!MS.active)
for(var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms)
if(!MS.use_power)
continue
return TRUE
return FALSE

View File

@@ -1,725 +0,0 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
/*
The broadcaster sends processed messages to all radio devices in the game. They
do not have to be headsets; intercoms and station-bounced radios suffice.
They receive their message from a server after the message has been logged.
*/
var/list/recentmessages = list() // global list of recent messages broadcasted : used to circumvent massive radio spam
var/message_delay = 0 // To make sure restarting the recentmessages list is kept in sync
/obj/machinery/telecomms/broadcaster
name = "Subspace Broadcaster"
icon_state = "broadcaster"
desc = "A dish-shaped machine used to broadcast processed subspace signals."
density = 1
anchored = 1
idle_power_usage = 25
machinetype = 5
produces_heat = 0
delay = 7
circuitboard = "/obj/item/circuitboard/telecomms/broadcaster"
/obj/machinery/telecomms/broadcaster/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
// Don't broadcast rejected signals
if(signal.data["reject"])
return
if(signal.data["message"])
// Prevents massive radio spam
signal.data["done"] = 1 // mark the signal as being broadcasted
// Search for the original signal and mark it as done as well
var/datum/signal/original = signal.data["original"]
if(original)
original.data["done"] = 1
original.data["compression"] = signal.data["compression"]
original.data["level"] = signal.data["level"]
var/signal_message = "[signal.frequency]:[signal.data["message"]]:[signal.data["realname"]]"
if(signal_message in recentmessages)
return
recentmessages.Add(signal_message)
if(signal.data["slow"] > 0)
sleep(signal.data["slow"]) // simulate the network lag if necessary
signal.data["level"] |= listening_level
/** #### - Normal Broadcast - #### **/
if(signal.data["type"] == 0)
/* ###### Broadcast a message using signal.data ###### */
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"],,
signal.data["compression"], signal.data["level"], signal.frequency,
signal.data["verb"], signal.data["language"] )
/** #### - Simple Broadcast - #### **/
if(signal.data["type"] == 1)
/* ###### Broadcast a message using signal.data ###### */
Broadcast_SimpleMessage(signal.data["name"], signal.frequency,
signal.data["message"],null, null,
signal.data["compression"], listening_level)
/** #### - Artificial Broadcast - #### **/
// (Imitates a mob)
if(signal.data["type"] == 2)
/* ###### Broadcast a message using signal.data ###### */
// Parameter "data" as 4: AI can't track this person/mob
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency,
signal.data["verb"], signal.data["language"])
if(!message_delay)
message_delay = 1
spawn(10)
message_delay = 0
recentmessages = list()
/* --- Do a snazzy animation! --- */
flick("broadcaster_send", src)
/obj/machinery/telecomms/broadcaster/Destroy()
// In case message_delay is left on 1, otherwise it won't reset the list and people can't say the same thing twice anymore.
if(message_delay)
message_delay = 0
return ..()
/*
Basically just an empty shell for receiving and broadcasting radio messages. Not
very flexible, but it gets the job done.
*/
/obj/machinery/telecomms/allinone
name = "Telecommunications Mainframe"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "comm_server"
desc = "A compact machine used for portable subspace telecommuniations processing."
density = 1
anchored = 1
use_power = POWER_USE_OFF
idle_power_usage = 0
machinetype = 6
produces_heat = 0
var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel
var/list/listening_freqs = list()
/obj/machinery/telecomms/allinone/Initialize()
. = ..()
if(!listening_freqs)
listening_freqs = ANTAG_FREQS //Covers any updates to ANTAG_FREQS
desc += " It has an effective broadcast range of [overmap_range] grids on the overmap."
/obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal)
if(!on) // has to be on to receive messages
return
if(!check_receive_sector(signal) && !intercept) //Too far on the overmap to receive. Antag (intercept) don't care about sector checks.
return
if(is_freq_listening(signal)) // detect subspace signals
signal.data["done"] = 1 // mark the signal as being broadcasted
signal.data["compression"] = 0
// Search for the original signal and mark it as done as well
var/datum/signal/original = signal.data["original"]
if(original)
original.data["done"] = 1
if(signal.data["slow"] > 0)
sleep(signal.data["slow"]) // simulate the network lag if necessary
var/list/broadcast_levels = list()
if(!intercept)
broadcast_levels = list(z)
broadcast_levels += GetConnectedZlevels(z) //For multi-z away sites
else
broadcast_levels = list(0) //This lets antag headsets work everywhere
/* ###### Broadcast a message using signal.data ###### */
var/datum/radio_frequency/connection = signal.data["connection"]
if(connection.frequency in listening_freqs) //Regular broadcasts
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"],, signal.data["compression"], broadcast_levels, connection.frequency,
signal.data["verb"], signal.data["language"])
else
if(intercept) //Antag Broadcast intercepting station messages
Broadcast_Message(signal.data["connection"], signal.data["mob"],
signal.data["vmask"], signal.data["vmessage"],
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency,
signal.data["verb"], signal.data["language"])
/obj/machinery/telecomms/allinone/ship
listening_freqs = list(SHIP_FREQ)
//This goes on the station map so away ships can maintain radio contact.
//Regular telecomms machines cannot listen to broadcasts coming from non-station z-levels. If we did this, comms would be receiving a substantial amount of duplicated messages.
/obj/machinery/telecomms/allinone/ship/station_relay
name = "External Signal Receiver"
icon = 'icons/obj/machines/telecomms.dmi'
icon_state = "ntnet"
desc = "This device allows nearby third-party ships to maintain radio contact with their crew that are aboard the %STATIONNAME."
desc_info = "This device does not need to be linked to other telecommunications equipment; it will receive and broadcast on its own. It only needs to be powered."
use_power = POWER_USE_IDLE
idle_power_usage = 25
/obj/machinery/telecomms/allinone/ship/station_relay/Initialize()
. = ..()
desc = replacetext(desc, "%STATIONNAME", current_map.station_name)
/**
Here is the big, bad function that broadcasts a message given the appropriate
parameters.
@param connection:
The datum generated in radio.dm, stored in signal.data["connection"].
@param M:
Reference to the mob/speaker, stored in signal.data["mob"]
@param vmask:
Boolean value if the mob is "hiding" its identity via voice mask, stored in
signal.data["vmask"]
@param vmessage:
If specified, will display this as the message; such as "chimpering"
for monkies if the mob is not understood. Stored in signal.data["vmessage"].
@param radio:
Reference to the radio broadcasting the message, stored in signal.data["radio"]
@param message:
The actual string message to display to mobs who understood mob M. Stored in
signal.data["message"]
@param name:
The name to display when a mob receives the message. signal.data["name"]
@param job:
The name job to display for the AI when it receives the message. signal.data["job"]
@param realname:
The "real" name associated with the mob. signal.data["realname"]
@param vname:
If specified, will use this name when mob M is not understood. signal.data["vname"]
@param data:
If specified:
1 -- Will only broadcast to intercoms
2 -- Will only broadcast to intercoms and station-bounced radios
3 -- Broadcast to syndicate frequency
4 -- AI can't track down this person. Useful for imitation broadcasts where you can't find the actual mob
@param compression:
If 0, the signal is audible
If nonzero, the signal may be partially inaudible or just complete gibberish.
@param level:
The list of Z levels that the sending radio is broadcasting to. Having 0 in the list broadcasts on all levels
@param freq
The frequency of the signal
**/
/proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M,
var/vmask, var/vmessage, var/obj/item/device/radio/radio,
var/message, var/name, var/job, var/realname, var/vname,
var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/datum/language/speaking = null)
/* ###### Prepare the radio connection ###### */
var/display_freq = freq
var/list/obj/item/device/radio/radios = list()
// --- Broadcast only to intercom devices ---
if(data == 1)
for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"])
if(R.receive_range(display_freq, level) > -1)
radios += R
// --- Broadcast only to intercoms and station-bounced radios ---
else if(data == 2)
for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"])
if(istype(R, /obj/item/device/radio/headset))
continue
if(R.receive_range(display_freq, level) > -1)
radios += R
// --- Broadcast to antag radios! ---
else if(data == 3)
for(var/antag_freq in ANTAG_FREQS)
var/datum/radio_frequency/antag_connection = SSradio.return_frequency(antag_freq)
for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"])
if(R.receive_range(antag_freq, level) > -1)
radios += R
// --- Broadcast to ALL radio devices ---
else
for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"])
if(R.receive_range(display_freq, level) > -1)
radios += R
// --- Radio sounds. ---
for(var/obj/item/device/radio/R in radios)
if((R.last_radio_sound + 1 SECOND) < world.time && R != radio)
playsound(R.loc, 'sound/effects/radio_chatter.ogg', 2.5, 0, -6, required_asfx_toggles = ASFX_RADIO)
R.last_radio_sound = world.time
// Get a list of mobs who can hear from the radios we collected.
var/list/receive = get_mobs_in_radio_ranges(radios)
/* ###### Organize the receivers into categories for displaying the message ###### */
// Understood the message:
var/list/heard_masked = list() // masked name or no real name
var/list/heard_normal = list() // normal message
// Did not understand the message:
var/list/heard_voice = list() // voice message (ie "chimpers")
var/list/heard_garbled = list() // garbled message (ie "f*c* **u, **i*er!")
var/list/heard_gibberish= list() // completely screwed over message ie "F%! (O*# *#!<>&**%!")
for (var/mob/R in receive)
/* --- Loop through the receivers and categorize them --- */
if (R.client)
if(R.client.prefs)
if(!(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
continue
else
log_debug("Client prefs found to be null in /proc/Broadcast_Message() for mob [R] and client [R.ckey], this should be investigated.")
if(istype(R, /mob/abstract/new_player)) // we don't want new players to hear messages. rare but generates runtimes.
continue
// Ghosts hearing all radio chat don't want to hear syndicate intercepts, they're duplicates
if(data == 3 && istype(R, /mob/abstract/observer) && R.client && R.client.prefs && (R.client.prefs.toggles & CHAT_GHOSTRADIO))
continue
// --- Check for compression ---
if(compression > 0)
heard_gibberish += R
continue
// --- Can understand the speech ---
if (!M || R.say_understands(M, speaking))
// - Not human or wearing a voice mask -
if (!M || !ishuman(M) || vmask)
heard_masked += R
// - Human and not wearing voice mask -
else
heard_normal += R
// --- Can't understand the speech ---
else
// - The speaker has a prespecified "voice message" to display if not understood -
if (vmessage)
heard_voice += R
// - Just display a garbled message -
else
heard_garbled += R
/* ###### Begin formatting and sending the message ###### */
if (length(heard_masked) || length(heard_normal) || length(heard_voice) || length(heard_garbled) || length(heard_gibberish))
/* --- Some miscellaneous variables to format the string output --- */
var/freq_text = get_frequency_name(display_freq)
var/part_b_extra = ""
if(data == 3) // intercepted radio message
part_b_extra = " <i>(Intercepted)</i>"
var/part_a = "<span class='[frequency_span_class(display_freq)]'>%ACCENT%<b>\[[freq_text]\][part_b_extra]</b> <span class='name'>" // goes in the actual output
// --- Some more pre-message formatting ---
var/part_b = "</span> <span class='message'></span>" // Tweaked for security headsets -- TLE
var/part_c = "</span>"
// --- Filter the message; place it in quotes apply a verb ---
var/quotedmsg = null
if(M)
quotedmsg = M.say_quote(message)
else
quotedmsg = "says, \"[message]\""
// --- This following recording is intended for research and feedback in the use of department radio channels ---
var/part_blackbox_b = "</span><b> \[[freq_text]\]</b> <span class='message'>" // Tweaked for security headsets -- TLE
var/blackbox_msg = "[part_a][name][part_blackbox_b][quotedmsg][part_c]"
//var/blackbox_admin_msg = "[part_a][M.name] (Real name: [M.real_name])[part_blackbox_b][quotedmsg][part_c]"
//BR.messages_admin += blackbox_admin_msg
if(istype(SSfeedback))
switch(display_freq)
if(PUB_FREQ)
SSfeedback.msg_common += blackbox_msg
if(SCI_FREQ)
SSfeedback.msg_science += blackbox_msg
if(COMM_FREQ)
SSfeedback.msg_command += blackbox_msg
if(MED_FREQ)
SSfeedback.msg_medical += blackbox_msg
if(ENG_FREQ)
SSfeedback.msg_engineering += blackbox_msg
if(SEC_FREQ)
SSfeedback.msg_security += blackbox_msg
if(DTH_FREQ)
SSfeedback.msg_deathsquad += blackbox_msg
if(SYND_FREQ)
SSfeedback.msg_syndicate += blackbox_msg
if(RAID_FREQ)
SSfeedback.msg_raider += blackbox_msg
if(NINJ_FREQ)
SSfeedback.msg_ninja += blackbox_msg
if(BLSP_FREQ)
SSfeedback.msg_bluespace += blackbox_msg
if(BURG_FREQ)
SSfeedback.msg_burglar += blackbox_msg
if(SUP_FREQ)
SSfeedback.msg_cargo += blackbox_msg
if(SRV_FREQ)
SSfeedback.msg_service += blackbox_msg
if(SHIP_FREQ)
SSfeedback.msg_ship += blackbox_msg
else
SSfeedback.messages += blackbox_msg
//End of research and feedback code.
/* ###### Send the message ###### */
/* --- Process all the mobs that heard a masked voice (understood) --- */
//Note that accent tags are handled in hear_radio.
if (length(heard_masked))
for (var/mob/R in heard_masked)
R.hear_radio(message, verbage, speaking, part_a, part_b, part_c, M, 0, name)
/* --- Process all the mobs that heard the voice normally (understood) --- */
if (length(heard_normal))
for (var/mob/R in heard_normal)
R.hear_radio(message, verbage, speaking, part_a, part_b, part_c, M, 0, realname)
/* --- Process all the mobs that heard the voice normally (did not understand) --- */
if (length(heard_voice))
for (var/mob/R in heard_voice)
R.hear_radio(message,verbage, speaking, part_a, part_b, part_c, M,0, vname)
/* --- Process all the mobs that heard a garbled voice (did not understand) --- */
// Displays garbled message (ie "f*c* **u, **i*er!")
if (length(heard_garbled))
for (var/mob/R in heard_garbled)
R.hear_radio(message, verbage, speaking, part_a, part_b, part_c, M, 1, vname)
/* --- Complete gibberish. Usually happens when there's a compressed message --- */
if (length(heard_gibberish))
for (var/mob/R in heard_gibberish)
R.hear_radio(message, verbage, speaking, part_a, part_b, part_c, M, 1)
return 1
/proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level)
/* ###### Prepare the radio connection ###### */
if(!M)
var/mob/living/carbon/human/H = new
M = H
var/datum/radio_frequency/connection = SSradio.return_frequency(frequency)
var/display_freq = connection.frequency
var/list/receive = list()
// --- Broadcast only to intercom devices ---
if(data == 1)
for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"])
var/turf/position = get_turf(R)
if(position && position.z == level)
receive |= R.send_hear(display_freq, level)
// --- Broadcast only to intercoms and station-bounced radios ---
else if(data == 2)
for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"])
if(istype(R, /obj/item/device/radio/headset))
continue
var/turf/position = get_turf(R)
if(position && position.z == level)
receive |= R.send_hear(display_freq)
// --- Broadcast to antag radios! ---
else if(data == 3)
for(var/freq in ANTAG_FREQS)
var/datum/radio_frequency/antag_connection = SSradio.return_frequency(freq)
for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"])
var/turf/position = get_turf(R)
if(position && position.z == level)
receive |= R.send_hear(freq)
// --- Broadcast to ALL radio devices ---
else
for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"])
var/turf/position = get_turf(R)
if(position && position.z == level)
receive |= R.send_hear(display_freq)
/* ###### Organize the receivers into categories for displaying the message ###### */
// Understood the message:
var/list/heard_normal = list() // normal message
// Did not understand the message:
var/list/heard_garbled = list() // garbled message (ie "f*c* **u, **i*er!")
var/list/heard_gibberish= list() // completely screwed over message (ie "F%! (O*# *#!<>&**%!")
for (var/mob/R in receive)
/* --- Loop through the receivers and categorize them --- */
if (R.client)
if(R.client.prefs)
if(!(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios.
continue
else
log_debug("Client prefs found to be null in /proc/Broadcast_SimpleMessage() for mob [R] and client [R.ckey], this should be investigated.")
// --- Check for compression ---
if(compression > 0)
heard_gibberish += R
continue
// --- Can understand the speech ---
if (R.say_understands(M))
heard_normal += R
// --- Can't understand the speech ---
else
// - Just display a garbled message -
heard_garbled += R
/* ###### Begin formatting and sending the message ###### */
if (length(heard_normal) || length(heard_garbled) || length(heard_gibberish))
/* --- Some miscellaneous variables to format the string output --- */
var/part_a = "<span class='[frequency_span_class(display_freq)]'><span class='name'>" // goes in the actual output
var/freq_text = get_frequency_name(display_freq)
// --- Some more pre-message formatting ---
var/part_b_extra = ""
if(data == 3) // intercepted radio message
part_b_extra = " <i>(Intercepted)</i>"
var/part_b = "</span><b> \[[freq_text]\][part_b_extra]</b> <span class='message'>" // Tweaked for security headsets -- TLE
var/part_blackbox_b = "</span><b> \[[freq_text]\]</b> <span class='message'>" // Tweaked for security headsets -- TLE
var/part_c = "</span></span>"
var/blackbox_msg = "[part_a][source][part_blackbox_b]\"[text]\"[part_c]"
//BR.messages_admin += blackbox_admin_msg
if(istype(blackbox))
switch(display_freq)
if(PUB_FREQ)
SSfeedback.msg_common += blackbox_msg
if(SCI_FREQ)
SSfeedback.msg_science += blackbox_msg
if(COMM_FREQ)
SSfeedback.msg_command += blackbox_msg
if(MED_FREQ)
SSfeedback.msg_medical += blackbox_msg
if(ENG_FREQ)
SSfeedback.msg_engineering += blackbox_msg
if(SEC_FREQ)
SSfeedback.msg_security += blackbox_msg
if(DTH_FREQ)
SSfeedback.msg_deathsquad += blackbox_msg
if(SYND_FREQ)
SSfeedback.msg_syndicate += blackbox_msg
if(RAID_FREQ)
SSfeedback.msg_raider += blackbox_msg
if(NINJ_FREQ)
SSfeedback.msg_ninja += blackbox_msg
if(BLSP_FREQ)
SSfeedback.msg_bluespace += blackbox_msg
if(BURG_FREQ)
SSfeedback.msg_burglar += blackbox_msg
if(SUP_FREQ)
SSfeedback.msg_cargo += blackbox_msg
if(SRV_FREQ)
SSfeedback.msg_service += blackbox_msg
if(SHIP_FREQ)
SSfeedback.msg_ship += blackbox_msg
else
SSfeedback.messages += blackbox_msg
//End of research and feedback code.
/* ###### Send the message ###### */
/* --- Process all the mobs that heard the voice normally (understood) --- */
if (length(heard_normal))
var/rendered = "[part_a][source][part_b]\"[text]\"[part_c]"
for (var/mob/R in heard_normal)
R.show_message(rendered, 2)
/* --- Process all the mobs that heard a garbled voice (did not understand) --- */
// Displays garbled message (ie "f*c* **u, **i*er!")
if (length(heard_garbled))
var/quotedmsg = "\"[stars(text)]\""
var/rendered = "[part_a][source][part_b][quotedmsg][part_c]"
for (var/mob/R in heard_garbled)
R.show_message(rendered, 2)
/* --- Complete gibberish. Usually happens when there's a compressed message --- */
if (length(heard_gibberish))
var/quotedmsg = "\"[Gibberish(text, compression + 50)]\""
var/rendered = "[part_a][Gibberish(source, compression + 50)][part_b][quotedmsg][part_c]"
for (var/mob/R in heard_gibberish)
R.show_message(rendered, 2)
//Use this to test if an obj can communicate with a Telecommunications Network
/atom/proc/test_telecomms()
var/datum/signal/signal = src.telecomms_process()
var/turf/position = get_turf(src)
return (position.z in signal.data["level"] && signal.data["done"])
/atom/proc/telecomms_process(var/do_sleep = 1)
// First, we want to generate a new radio signal
var/datum/signal/signal = new
signal.transmission_method = TRANSMISSION_SUBSPACE
var/turf/pos = get_turf(src)
// --- Finally, tag the actual signal with the appropriate values ---
signal.data = list(
"slow" = 0, // how much to sleep() before broadcasting - simulates net lag
"message" = "TEST",
"compression" = rand(45, 50), // If the signal is compressed, compress our message too.
"traffic" = 0, // dictates the total traffic sum that the signal went through
"type" = 4, // determines what type of radio input it is: test broadcast
"reject" = 0,
"done" = 0,
"level" = pos.z // The level it is being broadcasted at.
)
signal.frequency = PUB_FREQ// Common channel
//#### Sending the signal to all subspace receivers ####//
for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
R.receive_signal(signal)
if(do_sleep)
sleep(rand(10,25))
//world.log << "Level: [signal.data["level"]] - Done: [signal.data["done"]]"
return signal
/proc/telecomms_process_active(var/level = 5)
// First, we want to generate a new radio signal
var/datum/signal/signal = new
signal.transmission_method = TRANSMISSION_SUBSPACE
// --- Finally, tag the actual signal with the appropriate values ---
signal.data = list(
"slow" = 0, // how much to sleep() before broadcasting - simulates net lag
"message" = "TEST",
"compression" = rand(45, 50), // If the signal is compressed, compress our message too.
"traffic" = 0, // dictates the total traffic sum that the signal went through
"type" = 4, // determines what type of radio input it is: test broadcast
"reject" = 0,
"done" = 0,
"level" = level // The level it is being broadcasted at.
)
signal.frequency = PUB_FREQ// Common channel
//#### Sending the signal to all subspace receivers ####//
for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
R.receive_signal(signal)
return signal

View File

@@ -0,0 +1,259 @@
// Subtype of /datum/signal with additional processing information.
/datum/signal/subspace
transmission_method = TRANSMISSION_SUBSPACE
var/server_type = /obj/machinery/telecomms/server
var/datum/signal/subspace/original
var/origin_level
var/list/levels
var/obj/effect/overmap/visitable/sector
/datum/signal/subspace/New(obj/source, frequency, message = "", data = null)
src.source = source
src.frequency = frequency
var/turf/T = get_turf(source)
if(isturf(T))
origin_level = T.z
if(data)
src.data = data
else
src.data = list(
"compression" = rand(35, 65),
"message" = message
)
if(current_map.use_overmap && istype(source))
sector = map_sectors["[source.z]"]
/datum/signal/subspace/proc/copy()
var/datum/signal/subspace/copy = new
copy.original = src
copy.source = source
copy.levels = levels
copy.origin_level = origin_level
copy.frequency = frequency
copy.server_type = server_type
copy.transmission_method = transmission_method
copy.sector = sector
copy.data = data.Copy()
return copy
/datum/signal/subspace/proc/mark_done()
var/datum/signal/subspace/current = src
while (current)
current.data["done"] = TRUE
current = current.original
/datum/signal/subspace/proc/send_to_receivers()
if(!source.loc)
// It's an announcer message, just send it to the horizon's receiver
for(var/obj/machinery/telecomms/receiver/R in SSmachinery.all_receivers)
if(R.z in current_map.station_levels)
R.receive_signal(src)
return
var/closest_range = 999999
var/list/candidates = list()
var/obj/machinery/telecomms/selected_receiver
var/t_range = -1
for(var/obj/machinery/telecomms/R in SSmachinery.all_receivers)
t_range = R.receive_range(src)
if(t_range <= -1)
continue
if(t_range < closest_range)
candidates = list(R)
closest_range = t_range
else if(t_range == closest_range)
candidates |= R
if(!length(candidates))
return
if(length(candidates) > 1)
// Unlikely we ever have two receivers at the same distance listening to one frequency, but still
closest_range = 128 // get_dist returns 127 max
t_range = -1
for(var/obj/machinery/telecomms/R in candidates)
t_range = get_dist(R, source)
if(t_range < closest_range)
selected_receiver = R
closest_range = t_range
continue
else
selected_receiver = candidates[1]
selected_receiver.receive_signal(src)
/datum/signal/subspace/proc/broadcast()
set waitfor = FALSE
// Vocal transmissions (i.e. using saycode).
// Despite "subspace" in the name, these transmissions can also be RADIO
// (intercoms and SBRs) or SUPERSPACE (CentCom).
/datum/signal/subspace/vocal
var/datum/weakref/speaker
var/datum/language/language
/datum/signal/subspace/vocal/New(obj/source, frequency, datum/weakref/speaker, datum/language/language, message, say_verb)
src.source = source
src.frequency = frequency
src.language = language
src.speaker = speaker
var/turf/T = get_turf(source)
if(isturf(T))
origin_level = T.z
levels = list(T.z)
if(current_map.use_overmap)
sector = map_sectors["[T.z]"]
else // if the source is in nullspace, it's probably an autosay
levels = current_map.station_levels
origin_level = levels[1]
sector = map_sectors["[levels[1]]"]
var/mob/M = speaker.resolve()
data = list(
"name" = M.name,
"job" = M.job,
"message" = message,
"compression" = rand(35, 65),
"language" = language,
"say_verb" = say_verb
)
/datum/signal/subspace/vocal/copy()
var/datum/signal/subspace/vocal/copy = new(source, frequency, speaker, language)
copy.original = src
copy.data = data.Copy()
copy.levels = levels
return copy
// THE MEAT for making radios hear vocal transmissions.
/datum/signal/subspace/vocal/broadcast()
set waitfor = FALSE
var/message = copytext(data["message"], 1, MAX_MESSAGE_LEN)
if(!message || message == "")
return
var/list/signal_reaches_every_z_level = levels
if(0 in levels)
signal_reaches_every_z_level = RADIO_NO_Z_LEVEL_RESTRICTION
var/list/radios = list()
switch (transmission_method)
if (TRANSMISSION_SUBSPACE)
// Reach any radios on the levels
var/list/all_radios_of_our_frequency = SSradio.get_devices(frequency, RADIO_CHAT)
radios = all_radios_of_our_frequency.Copy()
for (var/obj/item/device/radio/subspace_radio in radios)
if(!subspace_radio.can_receive(frequency, signal_reaches_every_z_level))
radios -= subspace_radio
// Cool antag radios can hear all Horizon comms
for (var/antag_freq in list(SYND_FREQ, RAID_FREQ, NINJ_FREQ))
for (var/obj/item/device/radio/syndicate_radios in SSradio.get_devices(antag_freq, RADIO_CHAT))
if(syndicate_radios.can_receive(antag_freq, signal_reaches_every_z_level))
radios |= syndicate_radios
if (TRANSMISSION_RADIO)
// Only radios not in subspace mode
for (var/obj/item/device/radio/non_subspace_radio in SSradio.get_devices(frequency, RADIO_CHAT))
if(!non_subspace_radio.subspace_transmission && non_subspace_radio.can_receive(frequency, levels))
radios += non_subspace_radio
if (TRANSMISSION_SUPERSPACE)
// Independent radios
for (var/obj/item/device/radio/indie_radio in SSradio.get_devices(frequency, RADIO_CHAT))
if(indie_radio.independent && indie_radio.can_receive(frequency, levels))
radios += indie_radio
var/list/receive = get_hearers_in_radio_ranges(radios)
// Cut out admins which have radio chatter disabled
for (var/mob/R in receive)
if(R.client && R.client.holder && !(R.client.prefs.toggles & CHAT_RADIO))
receive -= R
// Add observers who have ghost radio enabled
for (var/mob/abstract/observer/M in player_list)
if(M.client && (M.client.prefs?.toggles & CHAT_GHOSTRADIO))
receive |= M
/* --- Some miscellaneous variables to format the string output --- */
var/freq_text = get_frequency_name(frequency)
var/part_b_extra = ""
if(data == 3) // intercepted radio message
part_b_extra = " <i>(Intercepted)</i>"
var/part_a = "<span class='[frequency_span_class(frequency)]'>%ACCENT%<b>\[[freq_text]\][part_b_extra]</b> <span class='name'>" // goes in the actual output
// --- Some more pre-message formatting ---
var/part_b = "</span> <span class='message'></span>" // Tweaked for security headsets -- TLE
var/part_c = "</span>"
// --- Filter the message; place it in quotes apply a verb ---
var/mob/M = speaker.resolve()
var/quotedmsg = null
if(M)
quotedmsg = M.say_quote(message)
else
quotedmsg = "says, \"[message]\""
var/compression = data["compression"]
for (var/mob/hearer in receive)
if(!hearer)
crash_with("null found in the hearers list returned by the spatial grid")
continue
hearer.hear_radio(message, data["say_verb"], language, part_a, part_b, part_c, M, !!compression)
// --- This following recording is intended for research and feedback in the use of department radio channels ---
var/part_blackbox_b = "</span><b> \[[freq_text]\]</b> <span class='message'>" // Tweaked for security headsets -- TLE
var/blackbox_msg = "[part_a][data["name"]][part_blackbox_b][quotedmsg][part_c]"
if(istype(SSfeedback))
switch(frequency)
if(PUB_FREQ)
SSfeedback.msg_common += blackbox_msg
if(SCI_FREQ)
SSfeedback.msg_science += blackbox_msg
if(COMM_FREQ)
SSfeedback.msg_command += blackbox_msg
if(MED_FREQ)
SSfeedback.msg_medical += blackbox_msg
if(ENG_FREQ)
SSfeedback.msg_engineering += blackbox_msg
if(SEC_FREQ)
SSfeedback.msg_security += blackbox_msg
if(DTH_FREQ)
SSfeedback.msg_deathsquad += blackbox_msg
if(SYND_FREQ)
SSfeedback.msg_syndicate += blackbox_msg
if(RAID_FREQ)
SSfeedback.msg_raider += blackbox_msg
if(NINJ_FREQ)
SSfeedback.msg_ninja += blackbox_msg
if(BLSP_FREQ)
SSfeedback.msg_bluespace += blackbox_msg
if(BURG_FREQ)
SSfeedback.msg_burglar += blackbox_msg
if(SUP_FREQ)
SSfeedback.msg_cargo += blackbox_msg
if(SRV_FREQ)
SSfeedback.msg_service += blackbox_msg
else
if(frequency in AWAY_FREQS_ASSIGNED)
SSfeedback.msg_ship += blackbox_msg
else
SSfeedback.messages += blackbox_msg

View File

@@ -61,7 +61,7 @@
else
dat += "<br>Total recorded traffic: [SelectedServer.totaltraffic] Gigabytes<br><br>"
dat += log_entries_to_text(SelectedServer)
dat += log_entries_to_text(user, SelectedServer)
user << browse(dat, "window=comm_monitor;size=575x400")
onclose(user, "server_control")
@@ -183,7 +183,7 @@
src.updateUsrDialog()
return 1
/obj/machinery/computer/telecomms/server/proc/log_entries_to_text(var/obj/machinery/telecomms/server/SelectedServer, start = 1, end = SelectedServer.log_entries.len)
/obj/machinery/computer/telecomms/server/proc/log_entries_to_text(mob/user, var/obj/machinery/telecomms/server/SelectedServer, start = 1, end = SelectedServer.log_entries.len)
if(!end)
end = SelectedServer.log_entries.len
start = between(1, start, SelectedServer.log_entries.len)
@@ -201,28 +201,25 @@
. += "<li><font color = #008F00>[C.name]</font> <font color = #FF0000><a href='?src=\ref[src];delete=[i]'>\[X\]</a></font><br>"
// -- Determine race of orator --
var/race = C.parameters["race"] // The actual race of the mob
var/language = C.parameters["language"] // The language spoken, or null/""
// -- If the orator is a human, or universal translate is active, OR mob has universal speech on --
if(universal_translate || C.parameters["uspeech"] || C.parameters["intelligible"])
. += "<u><font color = #18743E>Data type</font></u>: [C.input_type]<br>"
. += "<u><font color = #18743E>Source</font></u>: [C.parameters["name"]] (Job: [C.parameters["job"]])<br>"
. += "<u><font color = #18743E>Class</font></u>: [race]<br>"
. += "<u><font color = #18743E>Contents</font></u>: \"[C.parameters["message"]]\"<br>"
if(language)
. += "<u><font color = #18743E>Language</font></u>: [language]<br/>"
// -- Orator is not human and universal translate not active --
var/datum/language/language = C.parameters["language"]
var/message_out = ""
var/message_in = C.parameters["message"]
if(universal_translate || (language in user.languages))
message_out = "\"[message_in]\""
else if(!(language in user.languages))
// Language unknown by viewer, scramble
message_out = "\"[language.scramble(message_in, user.languages)]\""
else
. += "<u><font color = #18743E>Data type</font></u>: Audio File<br>"
. += "<u><font color = #18743E>Source</font></u>: <i>Unidentifiable</i><br>"
. += "<u><font color = #18743E>Class</font></u>: [race]<br>"
. += "<u><font color = #18743E>Contents</font></u>: <i>Unintelligble</i><br>"
// Entirely unintelligible
message_out = "(Unintelligible)"
. += "<u><font color = #18743E>Data type</font></u>: [C.input_type]<br>"
. += "<u><font color = #18743E>Source</font></u>: [C.parameters["name"]] [C.parameters["job"] ? "(Job: [C.parameters["job"]])" : ""]<br>"
. += "<u><font color = #18743E>Class</font></u>: [C.parameters["race"]]<br>"
. += "<u><font color = #18743E>Contents</font></u>: [message_out]<br>"
if(language)
. += "<u><font color = #18743E>Language</font></u>: [language.name]<br/>"
. += "</li><br>"

View File

@@ -1,5 +1,9 @@
// Allows you to monitor messages that passes the server.
/*
Monitoring computer for the messaging server.
Lets you read request console messages.
*/
// The monitor itself.
/obj/machinery/computer/message_monitor
name = "messaging monitor console"
desc = "Used to access and maintain data on messaging servers. Allows you to view requests console messages."
@@ -9,7 +13,7 @@
var/hack_icon = "error"
circuit = /obj/item/circuitboard/message_monitor
//Server linked to.
var/obj/machinery/message_server/linkedServer = null
var/obj/machinery/telecomms/message_server/linkedServer = null
//Sparks effect - For emag
var/datum/effect_system/sparks/spark_system
//Messages - Saves me time if I want to change something.
@@ -54,18 +58,18 @@
// Will create sparks and print out the console's password. You will then have to wait a while for the console to be back online.
// It'll take more time if there's more characters in the password..
if(!emag && operable())
if(!isnull(src.linkedServer))
emag = 1
if(!isnull(linkedServer))
emag = TRUE
screen = 2
src.spark_system.queue()
spark_system.queue()
var/obj/item/paper/monitorkey/MK = new/obj/item/paper/monitorkey
MK.forceMove(src.loc)
MK.forceMove(loc)
// Will help make emagging the console not so easy to get away with.
MK.info += "<br><br><span class='warning'>£%@%(*$%&(£&?*(%&£/{}</span>"
addtimer(CALLBACK(src, .proc/UnmagConsole), 100 * length(linkedServer.decryptkey))
message = rebootmsg
update_icon()
return 1
return TRUE
else
to_chat(user, "<span class='notice'>A no server error appears on the screen.</span>")
@@ -77,11 +81,15 @@
..()
/obj/machinery/computer/message_monitor/Initialize()
. = ..()
//Is the server isn't linked to a server, and there's a server available, default it to the first one in the list.
..()
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/message_monitor/LateInitialize()
//If the server isn't linked to a server, and there's a server available, default it to the first one in the list.
if(!linkedServer)
if(message_servers && message_servers.len > 0)
linkedServer = message_servers[1]
for(var/obj/machinery/telecomms/message_server/S in SSmachinery.all_telecomms)
linkedServer = S
break
/obj/machinery/computer/message_monitor/attack_hand(var/mob/living/user as mob)
if(stat & (NOPOWER|BROKEN))
@@ -97,10 +105,10 @@
if(auth)
dat += "<h4><dd><A href='?src=\ref[src];auth=1'>&#09;<font color='green'>\[Authenticated\]</font></a>&#09;/"
dat += " Server Power: <A href='?src=\ref[src];active=1'>[src.linkedServer && src.linkedServer.active ? "<font color='green'>\[On\]</font>":"<span class='warning'>\[Off\]</span>"]</a></h4>"
dat += " Server Power: <A href='?src=\ref[src];active=1'>[src.linkedServer && src.linkedServer.use_power ? "<font color='green'>\[On\]</font>":"<span class='warning'>\[Off\]</span>"]</a></h4>"
else
dat += "<h4><dd><A href='?src=\ref[src];auth=1'>&#09;<span class='warning'>\[Unauthenticated\]</span></a>&#09;/"
dat += " Server Power: <u>[src.linkedServer && src.linkedServer.active ? "<font color='green'>\[On\]</font>":"<span class='warning'>\[Off\]</span>"]</u></h4>"
dat += " Server Power: <u>[src.linkedServer && src.linkedServer.use_power ? "<font color='green'>\[On\]</font>":"<span class='warning'>\[Off\]</span>"]</u></h4>"
if(hacking || emag)
screen = 2
@@ -272,13 +280,17 @@
//Turn the server on/off.
if (href_list["active"])
if(auth) linkedServer.active = !linkedServer.active
if(auth) linkedServer.use_power = !linkedServer.use_power
//Find a server
if (href_list["find"])
if(message_servers && message_servers.len > 1)
src.linkedServer = input(usr,"Please select a server.", "Select a server.", null) as null|anything in message_servers
var/list/message_servers = list()
for(var/obj/machinery/telecomms/message_server/M in SSmachinery.all_telecomms)
message_servers += M
if(message_servers.len > 1)
linkedServer = input(usr,"Please select a server.", "Select a server.", null) as null|anything in message_servers
message = "<span class='alert'>NOTICE: Server selected.</span>"
else if(message_servers && message_servers.len > 0)
else if(message_servers.len > 0)
linkedServer = message_servers[1]
message = "<span class='notice'>NOTICE: Only Single Server Detected - Server selected.</span>"
else
@@ -403,15 +415,14 @@
/obj/item/paper/monitorkey
//..()
name = "Monitor Decryption Key"
var/obj/machinery/message_server/server = null
var/obj/machinery/telecomms/message_server/server = null
/obj/item/paper/monitorkey/Initialize()
..()
return INITIALIZE_HINT_LATELOAD
/obj/item/paper/monitorkey/LateInitialize()
if(message_servers)
for(var/obj/machinery/message_server/server in message_servers)
for(var/obj/machinery/telecomms/message_server/server in SSmachinery.all_telecomms)
if(!isnull(server))
if(!isnull(server.decryptkey))
info = "<center><h2>Daily Key Reset</h2></center><br>The new message monitor key is '[server.decryptkey]'.<br>Please keep this a secret and away from unauthorized personnel.<br>If necessary, change the password to a more secure one."

View File

@@ -0,0 +1,161 @@
/*
Telecomms monitor tracks the overall trafficing of a telecommunications network
and displays a hierarchy of linked machines.
*/
/obj/machinery/computer/telecomms/monitor
name = "telecommunications monitor"
desc = "A monitor that tracks the overall traffic of a telecommunications network, and displays a hierarchy of linked machines."
icon_screen = "comm_monitor"
icon_keyboard = "green_key"
light_color = LIGHT_COLOR_GREEN
var/screen = 0 // the screen number:
var/list/machinelist = list() // the machines located by the computer
var/obj/machinery/telecomms/SelectedMachine
var/network = "NULL" // the network to probe
var/temp = "" // temporary feedback messages
/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
user.set_machine(src)
var/dat = "<TITLE>Telecommunications Monitor</TITLE><center><b>Telecommunications Monitor</b></center>"
switch(screen)
// --- Main Menu ---
if(0)
dat += "<br>[temp]<br><br>"
dat += "<br>Current Network: <a href='?src=\ref[src];network=1'>[network]</a><br>"
if(machinelist.len)
dat += "<br>Detected Network Entities:<ul>"
for(var/obj/machinery/telecomms/T in machinelist)
dat += "<li><a href='?src=\ref[src];viewmachine=[T.id]'>\ref[T] [T.name]</a> ([T.id])</li>"
dat += "</ul>"
dat += "<br><a href='?src=\ref[src];operation=release'>\[Flush Buffer\]</a>"
else
dat += "<a href='?src=\ref[src];operation=probe'>\[Probe Network\]</a>"
// --- Viewing Machine ---
if(1)
dat += "<br>[temp]<br>"
dat += "<center><a href='?src=\ref[src];operation=mainmenu'>\[Main Menu\]</a></center>"
dat += "<br>Current Network: [network]<br>"
dat += "Selected Network Entity: [SelectedMachine.name] ([SelectedMachine.id])<br>"
dat += "Linked Entities: <ol>"
for(var/obj/machinery/telecomms/T in SelectedMachine.links)
if(!T.hide)
dat += "<li><a href='?src=\ref[src];viewmachine=[T.id]'>\ref[T.id] [T.name]</a> ([T.id])</li>"
dat += "</ol>"
user << browse(dat, "window=comm_monitor;size=575x400")
onclose(user, "server_control")
temp = ""
return
/obj/machinery/computer/telecomms/monitor/Topic(href, href_list)
if(..())
return
add_fingerprint(usr)
usr.set_machine(src)
if(href_list["viewmachine"])
screen = 1
for(var/obj/machinery/telecomms/T in machinelist)
if(T.id == href_list["viewmachine"])
SelectedMachine = T
break
if(href_list["operation"])
switch(href_list["operation"])
if("release")
machinelist = list()
screen = 0
if("mainmenu")
screen = 0
if("probe")
if(machinelist.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font>"
else
for(var/obj/machinery/telecomms/T in range(25, src))
if(T.network == network)
machinelist.Add(T)
if(!machinelist.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\] -</font>"
else
temp = "<font color = #336699>- [machinelist.len] ENTITIES LOCATED & BUFFERED -</font>"
screen = 0
if(href_list["network"])
var/newnet = sanitize(input(usr, "Which network do you want to view?", "Comm Monitor", network) as null|text)
if(newnet && ((usr in range(1, src) || issilicon(usr))))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font>"
else
network = newnet
screen = 0
machinelist = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font>"
updateUsrDialog()
return
/obj/machinery/computer/telecomms/monitor/attackby(var/obj/item/D as obj, var/mob/user as mob)
if(D.isscrewdriver())
if(D.use_tool(src, user, 20, volume = 50))
if (src.stat & BROKEN)
to_chat(user, "<span class='notice'>The broken glass falls out.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
new /obj/item/material/shard( src.loc )
var/obj/item/circuitboard/comm_monitor/M = new /obj/item/circuitboard/comm_monitor( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 3
A.icon_state = "3"
A.anchored = 1
qdel(src)
else
to_chat(user, "<span class='notice'>You disconnect the monitor.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
var/obj/item/circuitboard/comm_monitor/M = new /obj/item/circuitboard/comm_monitor( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 4
A.icon_state = "4"
A.anchored = 1
qdel(src)
src.updateUsrDialog()
return
/obj/machinery/computer/telecomms/monitor/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, "<span class='notice'>You you disable the security protocols</span>")
src.updateUsrDialog()
return 1

View File

@@ -0,0 +1,238 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32
/obj/machinery/computer/telecomms/traffic
name = "telecommunications traffic control"
icon_screen = "computer_generic"
icon_keyboard = "green_key"
light_color = LIGHT_COLOR_GREEN
req_access = list(access_tcomsat)
var/screen = 0 // the screen number:
var/list/servers = list() // the servers located by the computer
var/mob/editingcode
var/mob/lasteditor
var/list/viewingcode = list()
var/obj/machinery/telecomms/server/SelectedServer
var/network = "NULL" // the network to probe
var/temp = "" // temporary feedback messages
var/storedcode = "" // code stored
/obj/machinery/computer/telecomms/traffic/proc/update_ide()
// loop if there's someone manning the keyboard
while(editingcode)
if(!editingcode.client)
editingcode = null
break
// For the typer, the input is enabled. Buffer the typed text
if(editingcode)
storedcode = "[winget(editingcode, "tcscode", "text")]"
if(editingcode) // double if's to work around a runtime error
winset(editingcode, "tcscode", "is-disabled=false")
// If the player's not manning the keyboard anymore, adjust everything
if( (!(editingcode in range(1, src)) && !issilicon(editingcode)) || (editingcode.machine != src && !issilicon(editingcode)))
if(editingcode)
winshow(editingcode, "Telecomms IDE", 0) // hide the window!
editingcode = null
break
// For other people viewing the typer type code, the input is disabled and they can only view the code
// (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once)
if(length(viewingcode))
// This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
for(var/mob/M in viewingcode)
if((M.machine == src && (M in view(1, src))) || issilicon(M))
winset(M, "tcscode", "is-disabled=true")
winset(M, "tcscode", "text=\"[showcode]\"")
else
viewingcode.Remove(M)
winshow(M, "Telecomms IDE", 0) // hide the window!
sleep(5)
if(length(viewingcode) > 0)
editingcode = pick(viewingcode)
viewingcode.Remove(editingcode)
update_ide()
/obj/machinery/computer/telecomms/traffic/attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
user.set_machine(src)
var/dat = "<TITLE>Telecommunication Traffic Control</TITLE><center><b>Telecommunications Traffic Control</b></center>"
switch(screen)
// --- Main Menu ---
if(0)
dat += "<br>[temp]<br>"
dat += "<br>Current Network: <a href='?src=\ref[src];network=1'>[network]</a><br>"
if(servers.len)
dat += "<br>Detected Telecommunication Servers:<ul>"
for(var/obj/machinery/telecomms/T in servers)
dat += "<li><a href='?src=\ref[src];viewserver=[T.id]'>\ref[T] [T.name]</a> ([T.id])</li>"
dat += "</ul>"
dat += "<br><a href='?src=\ref[src];operation=release'>\[Flush Buffer\]</a>"
else
dat += "<br>No servers detected. Scan for servers: <a href='?src=\ref[src];operation=scan'>\[Scan\]</a>"
// --- Viewing Server ---
if(1)
dat += "<br>[temp]<br>"
dat += "<center><a href='?src=\ref[src];operation=mainmenu'>\[Main Menu\]</a> <a href='?src=\ref[src];operation=refresh'>\[Refresh\]</a></center>"
dat += "<br>Current Network: [network]"
dat += "<br>Selected Server: [SelectedServer.id]<br><br>"
dat += "<br><a href='?src=\ref[src];operation=editcode'>\[Edit Code\]</a>"
dat += "<br>Signal Execution: "
if(SelectedServer.autoruncode)
dat += "<a href='?src=\ref[src];operation=togglerun'>ALWAYS</a>"
else
dat += "<a href='?src=\ref[src];operation=togglerun'>NEVER</a>"
user << browse(dat, "window=traffic_control;size=575x400")
onclose(user, "server_control")
temp = ""
return
/obj/machinery/computer/telecomms/traffic/Topic(href, href_list)
if(..())
return
add_fingerprint(usr)
usr.set_machine(src)
if(!src.allowed(usr) && !emagged)
to_chat(usr, "<span class='warning'>ACCESS DENIED.</span>")
return
if(href_list["viewserver"])
screen = 1
for(var/obj/machinery/telecomms/T in servers)
if(T.id == href_list["viewserver"])
SelectedServer = T
break
if(href_list["operation"])
switch(href_list["operation"])
if("release")
servers = list()
screen = 0
if("mainmenu")
screen = 0
if("scan")
if(servers.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font>"
else
for(var/obj/machinery/telecomms/server/T in range(25, src))
if(T.network == network)
servers.Add(T)
if(!servers.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -</font>"
else
temp = "<font color = #336699>- [servers.len] SERVERS PROBED & BUFFERED -</font>"
screen = 0
if("editcode")
if(editingcode == usr) return
if(usr in viewingcode) return
if(!editingcode)
lasteditor = usr
editingcode = usr
winshow(editingcode, "Telecomms IDE", 1) // show the IDE
winset(editingcode, "tcscode", "is-disabled=false")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
winset(editingcode, "tcscode", "text=\"[showcode]\"")
spawn()
update_ide()
else
viewingcode.Add(usr)
winshow(usr, "Telecomms IDE", 1) // show the IDE
winset(usr, "tcscode", "is-disabled=true")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\"", "\\\"")
winset(usr, "tcscode", "text=\"[showcode]\"")
if("togglerun")
SelectedServer.autoruncode = !(SelectedServer.autoruncode)
if(href_list["network"])
var/newnet = sanitize(input(usr, "Which network do you want to view?", "Comm Monitor", network) as null|text)
if(newnet && ((usr in range(1, src)) || issilicon(usr)))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font>"
else
network = newnet
screen = 0
servers = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font>"
updateUsrDialog()
return
/obj/machinery/computer/telecomms/traffic/attackby(var/obj/item/D as obj, var/mob/user as mob)
if(D.isscrewdriver())
if(D.use_tool(src, user, 20, volume = 50))
if (src.stat & BROKEN)
to_chat(user, "<span class='notice'>The broken glass falls out.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
new /obj/item/material/shard( src.loc )
var/obj/item/circuitboard/comm_traffic/M = new /obj/item/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 3
A.icon_state = "3"
A.anchored = 1
qdel(src)
else
to_chat(user, "<span class='notice'>You disconnect the monitor.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
var/obj/item/circuitboard/comm_traffic/M = new /obj/item/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 4
A.icon_state = "4"
A.anchored = 1
qdel(src)
src.updateUsrDialog()
return
/obj/machinery/computer/telecomms/traffic/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, "<span class='notice'>You you disable the security protocols</span>")
src.updateUsrDialog()
return 1

View File

@@ -1,12 +1,3 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32
/*
All telecommunications interactions:
*/
/obj/machinery/telecomms
var/temp = "" // output message
var/construct_op = 0
@@ -26,9 +17,9 @@
if (integrity < 100) //Damaged, let's repair!
if (T.use(1))
integrity = between(0, initial(integrity) / 2, initial(integrity))
to_chat(usr, "You apply the Nanopaste to [src], repairing some of the damage.")
to_chat(user, "You apply the Nanopaste to [src], repairing some of the damage.")
else
to_chat(usr, "This machine is already in perfect condition.")
to_chat(user, "This machine is already in perfect condition.")
return TRUE
@@ -118,8 +109,8 @@
M = user.get_multitool()
var/dat
dat += "<br>[temp]<br><br>"
dat += "Power Status: <a href='?src=\ref[src];input=toggle'>[src.toggled ? "On" : "Off"]</a>"
if(on && toggled)
dat += "Power Status: <a href='?src=\ref[src];input=toggle'>[src.use_power ? "On" : "Off"]</a>"
if(operable() && use_power)
if(id != "" && id)
dat += "<br>Identification String: <a href='?src=\ref[src];input=id'>[id]</a>"
else
@@ -195,23 +186,6 @@
temp = "<font color = #666633>-% Processing mode changed. %-</font>"
src.process_mode = !src.process_mode
*/
// RELAY
/obj/machinery/telecomms/relay/Options_Menu()
var/dat = ""
dat += "<br>Broadcasting: <A href='?src=\ref[src];broadcast=1'>[broadcasting ? "YES" : "NO"]</a>"
dat += "<br>Receiving: <A href='?src=\ref[src];receive=1'>[receiving ? "YES" : "NO"]</a>"
return dat
/obj/machinery/telecomms/relay/Options_Topic(href, href_list)
if(href_list["receive"])
receiving = !receiving
temp = "<font color = #666633>-% Receiving mode changed. %-</font>"
if(href_list["broadcast"])
broadcasting = !broadcasting
temp = "<font color = #666633>-% Broadcasting mode changed. %-</font>"
// BUS
/obj/machinery/telecomms/bus/Options_Menu()
@@ -250,9 +224,8 @@
if("toggle")
src.toggled = !src.toggled
temp = "<font color = #666633>-% [src] has been [src.toggled ? "activated" : "deactivated"].</font>"
update_power()
toggle_power()
temp = "<font color = #666633>-% [src] has been [src.use_power ? "activated" : "deactivated"].</font>"
/*
if("hide")
@@ -275,7 +248,7 @@
else
for(var/obj/machinery/telecomms/T in links)
T.links.Remove(src)
remove_link(T)
network = newnet
links = list()
@@ -303,27 +276,17 @@
if(text2num(href_list["unlink"]) <= length(links))
var/obj/machinery/telecomms/T = links[text2num(href_list["unlink"])]
if(T)
temp = "<font color = #666633>-% Removed \ref[T] [T.name] from linked entities. %-</font>"
// Remove link entries from both T and src.
if(src in T.links)
T.links.Remove(src)
links.Remove(T)
remove_link(T)
if(href_list["link"])
if(P)
var/obj/machinery/telecomms/device = P.get_buffer()
if(istype(device) && device != src)
if(!(src in device.links))
device.links.Add(src)
if(!(device in src.links))
src.links.Add(device)
if(device)
add_new_link(device)
temp = "<font color = #666633>-% Successfully linked with \ref[device] [device.name] %-</font>"
else
temp = "<font color = #666633>-% Unable to acquire buffer %-</font>"
@@ -346,6 +309,37 @@
updateDialog()
// Adds new_connection to src's links list AND vice versa. also updates links_by_telecomms_type
/obj/machinery/telecomms/proc/add_new_link(obj/machinery/telecomms/new_connection)
if (!istype(new_connection) || new_connection == src)
return FALSE
if ((new_connection in links) && (src in new_connection.links))
return FALSE
links |= new_connection
new_connection.links |= src
LAZYADDASSOCLIST(links_by_telecomms_type, new_connection.telecomms_type, new_connection)
LAZYADDASSOCLIST(new_connection.links_by_telecomms_type, telecomms_type, src)
return TRUE
// Removes old_connection from src's links list AND vice versa. also updates links_by_telecomms_type
/obj/machinery/telecomms/proc/remove_link(obj/machinery/telecomms/old_connection)
if (!istype(old_connection) || old_connection == src)
return FALSE
if (old_connection in links)
links -= old_connection
LAZYREMOVEASSOC(links_by_telecomms_type, old_connection.telecomms_type, old_connection)
if (src in old_connection.links)
old_connection.links -= src
LAZYREMOVEASSOC(old_connection.links_by_telecomms_type, telecomms_type, src)
return TRUE
/obj/machinery/telecomms/proc/canAccess(var/mob/user)
if(issilicon(user) || in_range(user, src))
return 1

View File

@@ -0,0 +1,83 @@
/*
Basically just an empty shell for receiving and broadcasting radio messages. Not
very flexible, but it gets the job done.
*/
/obj/machinery/telecomms/allinone
name = "telecommunications mainframe"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "comm_server"
desc = "A compact machine used for portable subspace telecommuniations processing."
idle_power_usage = 0
active_power_usage = 0
produces_heat = FALSE
overmap_range = 2 // AIOs aren't true relays
var/away_aio = FALSE
var/list/recent_broadcasts
/obj/machinery/telecomms/allinone/Initialize()
. = ..()
if(!freq_listening.len)
freq_listening = ANTAG_FREQS
LAZYINITLIST(recent_broadcasts)
SSmachinery.all_receivers += src
desc += " It has an effective reception range of [overmap_range] grids on the overmap."
/obj/machinery/telecomms/allinone/Destroy()
SSmachinery.all_receivers -= src
return ..()
/obj/machinery/telecomms/allinone/receive_signal(datum/signal/subspace/signal)
signal.levels = broadcast_levels(signal)
// Decompress the signal, mark it received
signal.data["compression"] = 0
signal.mark_done()
var/signal_message = "[signal.frequency]:[signal.data["message"]]:[signal.data["realname"]]"
if(signal_message in recent_broadcasts)
return
LAZYADD(recent_broadcasts, signal_message)
if(signal.data["slow"] > 0)
addtimer(CALLBACK(signal, /datum/signal/subspace/proc/broadcast), signal.data["slow"]) // network lag
else
signal.broadcast()
spawn(10)
recent_broadcasts -= signal_message
/obj/machinery/telecomms/allinone/ship/LateInitialize()
. = ..()
if(!linked)
return
if(away_aio)
if(linked.comms_name)
name = "[lowertext(linked.comms_name)] [initial(name)]"
freq_listening = list(
HAIL_FREQ,
assign_away_freq(linked.name)
)
//This goes on the station map so away ships can maintain radio contact.
/obj/machinery/telecomms/allinone/ship/station_relay
name = "external signal receiver"
icon = 'icons/obj/machines/telecomms.dmi'
icon_state = "ntnet"
desc = "This device allows nearby third-party ships to maintain radio contact with their crew that are aboard the %STATIONNAME."
desc_info = "This device does not need to be linked to other telecommunications equipment; it will receive and broadcast on its own. It only needs to be powered."
idle_power_usage = 25
active_power_usage = 200
freq_listening = list(HAIL_FREQ)
away_aio = FALSE
/obj/machinery/telecomms/allinone/ship/station_relay/LateInitialize()
. = ..()
desc = replacetext(desc, "%STATIONNAME", current_map.station_name)
freq_listening |= AWAY_FREQS_ASSIGNED
freq_listening |= AWAY_FREQS_UNASSIGNED
freq_listening |= ANTAG_FREQS

View File

@@ -0,0 +1,55 @@
/*
The broadcaster sends processed messages to all radio devices in the game. They
do not have to be headsets; intercoms and station-bounced radios suffice.
They receive their message from a server after the message has been logged.
*/
/obj/machinery/telecomms/broadcaster
name = "subspace broadcaster"
icon_state = "broadcaster"
desc = "A dish-shaped machine used to broadcast processed subspace signals."
telecomms_type = /obj/machinery/telecomms/broadcaster
idle_power_usage = 100 // WATTS
active_power_usage = 3 KILOWATTS
produces_heat = FALSE
delay = 7
circuitboard = "/obj/item/circuitboard/telecomms/broadcaster"
var/list/recent_broadcasts
/obj/machinery/telecomms/broadcaster/Initialize(mapload)
. = ..()
LAZYINITLIST(recent_broadcasts)
/obj/machinery/telecomms/broadcaster/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from)
// Don't broadcast rejected signals
if(!istype(signal))
return
if(signal.data["reject"])
return
if(!signal.data["message"])
return
// Prevents massive radio spam
signal.mark_done()
var/datum/signal/subspace/original = signal.original
if(original && ("compression" in signal.data))
original.data["compression"] = signal.data["compression"]
signal.levels = broadcast_levels(signal)
var/signal_message = "[signal.frequency]:[signal.data["message"]]:[signal.data["realname"]]"
if(signal_message in recent_broadcasts)
return
LAZYADD(recent_broadcasts, signal_message)
if(signal.data["slow"] > 0)
addtimer(CALLBACK(signal, /datum/signal/subspace/proc/broadcast), signal.data["slow"]) // network lag
else
signal.broadcast()
/* --- Do a snazzy animation! --- */
flick("broadcaster_send", src)
spawn(10)
recent_broadcasts -= signal_message

View File

@@ -0,0 +1,43 @@
/*
The bus mainframe idles and waits for hubs to relay them signals. They act
as junctions for the network.
They transfer uncompressed subspace packets to processor units, and then take
the processed packet to a server for logging.
Link to a subspace hub if it can't send to a server.
*/
/obj/machinery/telecomms/bus
name = "bus mainframe"
icon_state = "bus"
desc = "A mighty piece of hardware used to send massive amounts of data quickly."
telecomms_type = /obj/machinery/telecomms/bus
idle_power_usage = 1 KILOWATTS
active_power_usage = 3 KILOWATTS
circuitboard = "/obj/item/circuitboard/telecomms/bus"
netspeed = 40
var/change_frequency = 0
/obj/machinery/telecomms/bus/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from)
if(!istype(signal) || !is_freq_listening(signal))
return
if(change_frequency && !(signal.frequency in ANTAG_FREQS))
signal.frequency = change_frequency
if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it
// send to one linked processor unit
if(relay_information(signal, /obj/machinery/telecomms/processor))
return
signal.data["slow"] += rand(1, 5) // Failed, relay it anyway, a little slower
// Try sending it!
var/list/try_send = list(signal.server_type, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster, /obj/machinery/telecomms/bus)
var/i = 0
for(var/send in try_send)
if(i)
signal.data["slow"] += rand(0,1)
i++
if(relay_information(signal, send))
break

View File

@@ -0,0 +1,31 @@
/*
The HUB idles until it receives information. It then passes on that information
depending on where it came from.
This is the heart of the Telecommunications Network, sending information where it
is needed. It mainly receives information from long-distance Relays and then sends
that information to be processed. Afterwards it gets the uncompressed information
from Servers/Buses and sends that back to the relay, to then be broadcasted.
*/
/obj/machinery/telecomms/hub
name = "telecommunication hub"
icon_state = "hub"
desc = "A mighty piece of hardware used to send and receive massive amounts of data."
telecomms_type = /obj/machinery/telecomms/hub
density = TRUE
anchored = TRUE
idle_power_usage = 1.6 KILOWATTS
active_power_usage = 5 KILOWATTS
circuitboard = "/obj/item/circuitboard/telecomms/hub"
netspeed = 40
/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
if(!is_freq_listening(signal))
return
if(istype(machine_from, /obj/machinery/telecomms/receiver))
//If the signal is compressed, send it to the bus.
relay_information(signal, /obj/machinery/telecomms/bus) // ideally relay the copied information to bus units
else
relay_information(signal, /obj/machinery/telecomms/broadcaster) // Broadcast the current signal to our z-levels

View File

@@ -1,31 +1,48 @@
#define MESSAGE_SERVER_SPAM_REJECT 1
#define MESSAGE_SERVER_DEFAULT_SPAM_LIMIT 10
var/global/list/obj/machinery/message_server/message_servers = list()
// Log datums stored by the message server.
/datum/data_pda_msg
var/recipient = "Unspecified" //name of the person
var/sender = "Unspecified" //name of the sender
var/sender = "Unspecified"
var/recipient = "Unspecified"
var/message = "Blank" // transferred message
var/icon/photo // attached photo
/datum/data_pda_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "")
/datum/data_pda_msg/New(param_rec, param_sender, param_message, param_photo)
if(param_rec)
recipient = param_rec
if(param_sender)
sender = param_sender
if(param_message)
message = param_message
if(param_photo)
photo = param_photo
/datum/data_pda_msg/proc/get_photo_ref()
if(photo)
return "<a href='byond://?src=["\ref[src]"];photo=1'>(Photo)</a>"
return ""
/datum/data_pda_msg/Topic(href,href_list)
..()
if(href_list["photo"])
var/mob/M = usr
M << browse_rsc(photo, "pda_photo.png")
M << browse("<html><head><title>PDA Photo</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='pda_photo.png' width='192' style='-ms-interpolation-mode:nearest-neighbor' />" \
+ "</body></html>", "window=pdaphoto;size=192x192")
onclose(M, "pdaphoto")
/datum/data_rc_msg
var/rec_dpt = "Unspecified" //name of the person
var/send_dpt = "Unspecified" //name of the sender
var/message = "Blank" //transferred message
var/rec_dpt = "Unspecified" // receiving department
var/send_dpt = "Unspecified" // sending department
var/message = "Blank"
var/stamp = "Unstamped"
var/id_auth = "Unauthenticated"
var/priority = "Normal"
/datum/data_rc_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "",var/param_stamp = "",var/param_id_auth = "",var/param_priority)
/datum/data_rc_msg/New(param_rec, param_sender, param_message, param_stamp, param_id_auth, param_priority)
if(param_rec)
rec_dpt = param_rec
if(param_sender)
@@ -47,18 +64,17 @@ var/global/list/obj/machinery/message_server/message_servers = list()
else
priority = "Undetermined"
/obj/machinery/message_server
/obj/machinery/telecomms/message_server
icon = 'icons/obj/machines/research.dmi'
icon_state = "server"
name = "messaging server"
density = 1
anchored = 1.0
desc = "A machine that processes and routes request console messages."
telecomms_type = /obj/machinery/telecomms/message_server
idle_power_usage = 10
active_power_usage = 100
var/list/datum/data_pda_msg/pda_msgs = list()
var/list/datum/data_pda_msg/pda_msgs = list() // TODO: actually re-link modular PDAs to the message servers
var/list/datum/data_rc_msg/rc_msgs = list()
var/active = 1
var/decryptkey = "password"
//Spam filtering stuff
@@ -67,19 +83,13 @@ var/global/list/obj/machinery/message_server/message_servers = list()
//Messages having theese tokens will be rejected by server. Case sensitive
var/spamfilter_limit = MESSAGE_SERVER_DEFAULT_SPAM_LIMIT //Maximal amount of tokens
/obj/machinery/message_server/New()
message_servers += src
/obj/machinery/telecomms/message_server/Initialize()
. = ..()
if(!decryptkey)
decryptkey = GenerateKey()
send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.")
..()
return
// send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.")
/obj/machinery/message_server/Destroy()
message_servers -= src
return ..()
/obj/machinery/message_server/proc/GenerateKey()
/obj/machinery/telecomms/message_server/proc/GenerateKey()
//Feel free to move to Helpers.
var/newKey
newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le")
@@ -87,25 +97,20 @@ var/global/list/obj/machinery/message_server/message_servers = list()
newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
return newKey
/obj/machinery/message_server/process()
//if(decryptkey == "password")
// decryptkey = generateKey()
if(active && (stat & (BROKEN|NOPOWER)))
active = 0
return
update_icon()
/obj/machinery/telecomms/message_server/receive_information(datum/signal/subspace/pda/signal, obj/machinery/telecomms/machine_from)
// can't log non-PDA signals
if(!istype(signal) || !signal.data["message"] || !use_power || inoperable())
return
/obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "")
var/result
for (var/token in spamfilter)
if (findtextEx(message,token))
message = "<font color=\"red\">[message]</font>" //Rejected messages will be indicated by red color.
result = token //Token caused rejection (if there are multiple, last will be chosen)
pda_msgs += new/datum/data_pda_msg(recipient,sender,message)
return result
// log the signal
pda_msgs += new /datum/data_pda_msg(signal.format_target(), "[signal.data["name"]] ([signal.data["job"]])", signal.data["message"], signal.data["photo"])
signal.data -= "reject" // only gets through if it's logged
/obj/machinery/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1)
// pass it along to either the hub or the broadcaster
if(!relay_information(signal, /obj/machinery/telecomms/hub))
relay_information(signal, /obj/machinery/telecomms/broadcaster)
/obj/machinery/telecomms/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1)
rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth)
var/authmsg = "[message]<br>"
if (id_auth)
@@ -140,16 +145,16 @@ var/global/list/obj/machinery/message_server/message_servers = list()
Console.set_light(2)
/obj/machinery/message_server/attack_hand(user as mob)
/obj/machinery/telecomms/message_server/attack_hand(user as mob)
// to_chat(user, "\blue There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentCom delays.")
to_chat(user, "You toggle request console message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]")
active = !active
to_chat(user, "You toggle request console message passing from [use_power ? "On" : "Off"] to [use_power ? "Off" : "On"]")
toggle_power()
update_icon()
return
/obj/machinery/message_server/attackby(obj/item/O as obj, mob/living/user as mob)
if (active && !(stat & (BROKEN|NOPOWER)) && (spamfilter_limit < MESSAGE_SERVER_DEFAULT_SPAM_LIMIT*2) && \
/obj/machinery/telecomms/message_server/attackby(obj/item/O as obj, mob/living/user as mob)
if (use_power && !inoperable(EMPED) && (spamfilter_limit < MESSAGE_SERVER_DEFAULT_SPAM_LIMIT*2) && \
istype(O,/obj/item/circuitboard/message_monitor))
spamfilter_limit += round(MESSAGE_SERVER_DEFAULT_SPAM_LIMIT / 2)
user.drop_from_inventory(O,get_turf(src))
@@ -158,22 +163,66 @@ var/global/list/obj/machinery/message_server/message_servers = list()
else
..(O, user)
/obj/machinery/message_server/update_icon()
if((stat & (BROKEN|NOPOWER)))
/obj/machinery/telecomms/message_server/update_icon()
if(inoperable(EMPED))
icon_state = "server-nopower"
else if (!active)
else if (!use_power)
icon_state = "server-off"
else
icon_state = "server-on"
/datum/signal/subspace/pda
frequency = PUB_FREQ
server_type = /obj/machinery/telecomms/message_server
/datum/signal/subspace/pda/New(source, data)
src.source = source
src.data = data
var/turf/T = get_turf(source)
levels = list(T.z)
data["reject"] = TRUE // set to FALSE if a messaging server logs it
/datum/signal/subspace/pda/copy()
var/datum/signal/subspace/pda/copy = new(source, data.Copy())
copy.original = src
copy.levels = levels
return copy
/datum/signal/subspace/pda/proc/format_target()
if (length(data["targets"]) > 1)
return "Everyone"
return data["targets"][1]
/datum/signal/subspace/pda/proc/format_message()
if (data["photo"])
return "[data["message"]] <a href='byond://?src=["\ref[src]"];photo=1'>(Photo)</a>"
return data["message"]
/datum/signal/subspace/pda/broadcast()
// TODO: need an iterable list of available PDAs
// for (var/obj/item/modular_computer in SSmachinery.machinery)
// if ("[P.owner] ([P.ownjob])" in data["targets"])
// P.receive_message(src)
/datum/signal/subspace/pda/Topic(href, href_list)
..()
if (href_list["photo"])
var/mob/M = usr
M << browse_rsc(data["photo"], "pda_photo.png")
M << browse("<html><head><title>PDA Photo</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='pda_photo.png' width='192' style='-ms-interpolation-mode:nearest-neighbor' />" \
+ "</body></html>", "window=pdaphoto;size=192x192")
onclose(M, "pdaphoto")
var/obj/machinery/blackbox_recorder/blackbox
/obj/machinery/blackbox_recorder
icon = 'icons/obj/stationobjs.dmi'
icon_state = "blackbox"
name = "blackbox recorder"
density = 1
anchored = 1.0
density = TRUE
anchored = TRUE
idle_power_usage = 10
active_power_usage = 100

View File

@@ -0,0 +1,37 @@
/*
The processor is a very simple machine that decompresses subspace signals and
transfers them back to the original bus. It is essential in producing audible
data.
Link to servers if bus is not present
*/
#define COMPRESS 0
#define UNCOMPRESS 1
/obj/machinery/telecomms/processor
name = "processor unit"
icon_state = "processor"
desc = "This machine is used to process large quantities of information."
telecomms_type = /obj/machinery/telecomms/processor
delay = 5
circuitboard = "/obj/item/circuitboard/telecomms/processor"
var/process_mode = UNCOMPRESS
/obj/machinery/telecomms/processor/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from)
if(!is_freq_listening(signal))
return
if(!process_mode)
signal.data["compression"] = 100 // even more compressed signal
else if (signal.data["compression"])
signal.data["compression"] = 0 // uncompress subspace signal
if(istype(machine_from, /obj/machinery/telecomms/bus))
relay_direct_information(signal, machine_from) // send the signal back to the machine
else // no bus detected - send the signal to servers instead
signal.data["slow"] += rand(5, 10) // slow the signal down
relay_information(signal, signal.server_type)
#undef COMPRESS
#undef UNCOMPRESS

View File

@@ -0,0 +1,27 @@
/*
The receiver idles and receives messages from subspace-compatible radio equipment;
primarily headsets. Received messages are then passed to the network hub, or failing that,
the network bus for further processing and broadcast.
*/
/obj/machinery/telecomms/receiver
name = "subspace receiver"
icon_state = "broadcast receiver"
desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity."
telecomms_type = /obj/machinery/telecomms/receiver
produces_heat = FALSE
overmap_range = 2
circuitboard = "/obj/item/circuitboard/telecomms/receiver"
/obj/machinery/telecomms/receiver/Initialize(mapload)
. = ..()
SSmachinery.all_receivers += src
desc += " It has an effective reception range of [overmap_range] grids on the overmap."
/obj/machinery/telecomms/receiver/Destroy()
SSmachinery.all_receivers -= src
return ..()
/obj/machinery/telecomms/receiver/receive_signal(datum/signal/subspace/signal)
if(!relay_information(signal, /obj/machinery/telecomms/hub, TRUE))
relay_information(signal, /obj/machinery/telecomms/bus, TRUE)

View File

@@ -0,0 +1,104 @@
/*
The server logs all traffic and signal data. Once it records the signal, it sends
it to the subspace broadcaster.
Store a maximum of 400 logs and then deletes them.
*/
// Simple log entry datum
/datum/comm_log_entry
var/input_type = "Speech File"
var/name = "data packet (#)"
var/parameters = list()
/obj/machinery/telecomms/server
name = "telecommunication server"
icon_state = "comm_server"
desc = "A machine used to store data and network statistics."
telecomms_type = /obj/machinery/telecomms/server
density = TRUE
anchored = TRUE
idle_power_usage = 300 // WATTS
active_power_usage = 1 KILOWATTS
circuitboard = "/obj/item/circuitboard/telecomms/server"
var/list/log_entries = list()
var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes)
var/rawcode = "" // the code to compile (raw text)
var/datum/ntsl2_program/tcomm/Program // NTSL2++ datum responsible for script execution
var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up
/obj/machinery/telecomms/server/Initialize()
. = ..()
Program = SSntsl2.new_program_tcomm(src)
/obj/machinery/telecomms/server/receive_information(datum/signal/subspace/vocal/signal, obj/machinery/telecomms/machine_from)
// can't log non-vocal signals
if(!istype(signal) || !signal.data["message"] || !is_freq_listening(signal))
return
if(traffic > 0)
totaltraffic += traffic
// Delete particularly old logs
if (log_entries.len >= 400)
log_entries.Cut(1, 2)
var/datum/comm_log_entry/log = new
var/mob/speaker = signal.speaker.resolve()
if(!istype(speaker))
return
log.parameters["name"] = signal.data["name"]
log.parameters["job"] = signal.data["job"]
log.parameters["message"] = signal.data["message"]
log.parameters["language"] = signal.language || all_languages[LANGUAGE_TCB]
var/race = "Unidentifiable"
if(ishuman(speaker) || isbrain(speaker))
race = "Sapient Species"
else if(speaker.isMonkey())
race = "Monkey"
else if(issilicon(speaker) || isobj(speaker))
race = "Machinery"
else if(isslime(speaker))
race = "Slime"
else if(isanimal(speaker))
race = "Domestic Animal"
else if(istype(speaker, /mob/living/announcer))
race = "Announcement"
log.parameters["race"] = race
// If the signal is still compressed, make the log entry gibberish
var/compression = signal.data["compression"]
if (compression > 0)
log.input_type = "Corrupt File"
log.parameters["name"] = Gibberish(signal.data["name"], compression + 50)
log.parameters["job"] = Gibberish(signal.data["job"], compression + 50)
log.parameters["message"] = Gibberish(signal.data["message"], compression + 50)
// Give the log a name and store it
var/identifier = num2text ( rand(-1000, 1000) + world.time )
log.name = "data packet ([md5(identifier)])"
log_entries.Add(log)
if(istype(Program))
Program.process_message(signal, CALLBACK(src, .proc/program_receive_information, signal))
finish_receive_information(signal)
/obj/machinery/telecomms/server/proc/program_receive_information(datum/signal/signal)
Program.retrieve_messages(CALLBACK(src, .proc/finish_receive_information, signal))
/obj/machinery/telecomms/server/proc/finish_receive_information(datum/signal/signal)
var/can_send = relay_information(signal, /obj/machinery/telecomms/hub)
if(!can_send)
relay_information(signal, /obj/machinery/telecomms/broadcaster)
/obj/machinery/telecomms/server/process()
. = ..()
if(istype(Program))
Program.retrieve_messages()

View File

@@ -1,77 +1,107 @@
// ### Preset machines ###
//Relay
/obj/machinery/telecomms/relay/preset
network = "tcommsat"
/obj/machinery/telecomms/relay/preset/station
id = "Station Relay"
autolinkers = list("s_relay")
/obj/machinery/telecomms/relay/preset/telecomms
id = "Telecomms Relay"
autolinkers = list("relay")
/obj/machinery/telecomms/relay/preset/mining
id = "Mining Relay"
autolinkers = list("m_relay")
/obj/machinery/telecomms/relay/preset/ruskie
id = "Ruskie Relay"
hide = 1
toggled = 0
autolinkers = list("r_relay")
/obj/machinery/telecomms/relay/preset/centcom
id = "Centcom Relay"
hide = 1
toggled = 1
//anchored = 1
//use_power = POWER_USE_OFF
//idle_power_usage = 0
produces_heat = 0
autolinkers = list("c_relay")
//HUB
/obj/machinery/telecomms/hub/preset_map/LateInitialize()
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
if(istype(linked) && linked.comms_support)
var/preset_name = linked.comms_name
var/name_lower = replacetext(lowertext(preset_name), " ", "_")
id = "[preset_name] Hub"
network = "tcomm_[name_lower]"
autolinkers = list(
"[name_lower]_broadcaster",
"[name_lower]_hub",
"[name_lower]_receiver",
"[name_lower]_server"
)
return ..()
/obj/machinery/telecomms/hub/preset
id = "Hub"
network = "tcommsat"
autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "science", "medical",
"supply", "service", "common", "command", "engineering", "security", "unused",
autolinkers = list("hub", "science", "medical", "supply", "service", "common", "command", "engineering", "security", "unused",
"receiverA", "broadcasterA")
/obj/machinery/telecomms/hub/preset_cent
id = "CentComm Hub"
network = "tcommsat"
produces_heat = 0
autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay",
"centcomm", "receiverCent", "broadcasterCent")
autolinkers = list("hub_cent", "centcomm", "receiverCent", "broadcasterCent")
//Receivers
/obj/machinery/telecomms/receiver/preset_map
var/use_common = FALSE
/obj/machinery/telecomms/receiver/preset_map/LateInitialize()
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
if(istype(linked) && linked.comms_support)
var/preset_name = linked.comms_name
var/name_lower = replacetext(lowertext(preset_name), " ", "_")
id = "[preset_name] Receiver"
network = "tcomm_[name_lower]"
freq_listening += list(assign_away_freq(preset_name), HAIL_FREQ)
if (use_common)
freq_listening += PUB_FREQ
autolinkers = list(
"[name_lower]_receiver"
)
return ..()
/obj/machinery/telecomms/receiver/preset_right
id = "Receiver A"
network = "tcommsat"
autolinkers = list("receiverA") // link to relay
autolinkers = list("receiverA")
freq_listening = list(AI_FREQ, SCI_FREQ, MED_FREQ, SUP_FREQ, SRV_FREQ, COMM_FREQ, ENG_FREQ, SEC_FREQ, ENT_FREQ)
//Common and other radio frequencies for people to freely use
New()
/obj/machinery/telecomms/receiver/preset_right/Initialize()
for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2)
freq_listening |= i
..()
. = ..()
/obj/machinery/telecomms/receiver/preset_cent
id = "CentComm Receiver"
network = "tcommsat"
produces_heat = 0
produces_heat = FALSE
autolinkers = list("receiverCent")
freq_listening = list(ERT_FREQ, DTH_FREQ)
//Buses
/obj/machinery/telecomms/bus/preset_map
var/use_common = FALSE
/obj/machinery/telecomms/bus/preset_map/LateInitialize()
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
if(istype(linked) && linked.comms_support)
var/preset_name = linked.comms_name
var/name_lower = replacetext(lowertext(preset_name), " ", "_")
id = "[preset_name] Bus"
network = "tcomm_[name_lower]"
freq_listening += list(assign_away_freq(preset_name), HAIL_FREQ)
if (use_common)
freq_listening += PUB_FREQ
autolinkers = list(
"[name_lower]_processor",
"[name_lower]_server"
)
return ..()
/obj/machinery/telecomms/bus/preset_one
id = "Bus 1"
@@ -85,12 +115,12 @@
freq_listening = list(SUP_FREQ, SRV_FREQ)
autolinkers = list("processor2", "supply", "service", "unused")
/obj/machinery/telecomms/bus/preset_two/New()
/obj/machinery/telecomms/bus/preset_two/Initialize()
for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2)
if(i == PUB_FREQ)
if(i == PUB_FREQ || i == ENT_FREQ)
continue
freq_listening |= i
..()
return ..()
/obj/machinery/telecomms/bus/preset_three
id = "Bus 3"
@@ -101,18 +131,35 @@
/obj/machinery/telecomms/bus/preset_four
id = "Bus 4"
network = "tcommsat"
freq_listening = list(ENG_FREQ, AI_FREQ, PUB_FREQ, ENT_FREQ)
freq_listening = list(ENG_FREQ, AI_FREQ, PUB_FREQ, ENT_FREQ, HAIL_FREQ)
autolinkers = list("processor4", "engineering", "common")
/obj/machinery/telecomms/bus/preset_cent
id = "CentComm Bus"
network = "tcommsat"
freq_listening = list(ERT_FREQ, DTH_FREQ, ENT_FREQ)
produces_heat = 0
produces_heat = FALSE
autolinkers = list("processorCent", "centcomm")
//Processors
/obj/machinery/telecomms/processor/preset_map/LateInitialize()
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
if(istype(linked) && linked.comms_support)
var/preset_name = linked.comms_name
var/name_lower = replacetext(lowertext(preset_name), " ", "_")
id = "[preset_name] Processor"
network = "tcomm_[name_lower]"
autolinkers = list(
"[name_lower]_processor"
)
return ..()
/obj/machinery/telecomms/processor/preset_one
id = "Processor 1"
network = "tcommsat"
@@ -136,13 +183,37 @@
/obj/machinery/telecomms/processor/preset_cent
id = "CentComm Processor"
network = "tcommsat"
produces_heat = 0
produces_heat = FALSE
autolinkers = list("processorCent")
//Servers
/obj/machinery/telecomms/server/preset_map
var/use_common = FALSE
/obj/machinery/telecomms/server/preset_map/LateInitialize()
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
if(istype(linked) && linked.comms_support)
var/preset_name = linked.comms_name
var/name_lower = replacetext(lowertext(preset_name), " ", "_")
id = "[preset_name] server"
network = "tcomm_[name_lower]"
freq_listening += list(
assign_away_freq(preset_name),
HAIL_FREQ
)
if(use_common)
freq_listening += PUB_FREQ
autolinkers = list(
"[name_lower]_server"
)
return ..()
/obj/machinery/telecomms/server/presets
network = "tcommsat"
/obj/machinery/telecomms/server/presets/science
@@ -167,7 +238,7 @@
/obj/machinery/telecomms/server/presets/common
id = "Common Server"
freq_listening = list(PUB_FREQ, AI_FREQ, ENT_FREQ) // AI Private and Common
freq_listening = list(PUB_FREQ, AI_FREQ, ENT_FREQ, MED_I_FREQ, SEC_I_FREQ, HAIL_FREQ) // AI Private, Common, and Dept. Intercoms
autolinkers = list("common")
// "Unused" channels, AKA all others.
@@ -178,7 +249,7 @@
/obj/machinery/telecomms/server/presets/unused/New()
for(var/i = PUBLIC_LOW_FREQ, i < PUBLIC_HIGH_FREQ, i += 2)
if(i == AI_FREQ || i == PUB_FREQ)
if(i == AI_FREQ || i == PUB_FREQ || i == ENT_FREQ || i == MED_I_FREQ || i == SEC_I_FREQ || i == HAIL_FREQ)
continue
freq_listening |= i
..()
@@ -209,6 +280,23 @@
//--PRESET LEFT--//
/obj/machinery/telecomms/broadcaster/preset_map/LateInitialize()
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
if(istype(linked) && linked.comms_support)
var/preset_name = linked.comms_name
var/name_lower = replacetext(lowertext(preset_name), " ", "_")
id = "[preset_name] broadcaster"
network = "tcomm_[name_lower]"
autolinkers = list(
"[name_lower]_broadcaster"
)
return ..()
/obj/machinery/telecomms/broadcaster/preset_right
id = "Broadcaster A"
network = "tcommsat"

View File

@@ -0,0 +1,247 @@
/*
Telecommunications machines handle communications over radio and subspace networks, primarily for radio communication
but also for a variety of other networked machines.
Signals are received by the closest active and viable receiver, i.e. one listening on their frequency and powered. Only one receiver processes any given signal normally.
Hubs take incoming signals from receivers and passes them to the bus for processing, and take completed messages from the server to broadcast
Buses move signals from hub to processor, and then to the signal's designated server (usually the Tcomms server)
Processors decompress compressed signals (and vice versa), sending the processed signal back to the bus.
Servers log the processed radio messages and perform any NTSL functions on the messages before sending them to be broadcast via the hub.
Broadcasters take completed messages and send them to all available devices within their broadcasting range.
All-in-Ones, as the name suggests, provide all of this functionality (to some extent) in a compact package, capable of receiving and broadcasting signals to their own connected z-level(s).
AIOs by default only receive and broadcast communications on their own bespoke frequency and the hailing frequency.
The routing that radio-frequency machines take is relatively simple:
Inbound Signal -> Receiver -> Hub -> Bus -> Processor -> Bus -> Server -> Hub -> Broadcaster
*/
/obj/machinery/telecomms
icon = 'icons/obj/machines/telecomms.dmi'
density = TRUE
anchored = TRUE
idle_power_usage = 600 // WATTS
active_power_usage = 2 KILOWATTS
var/list/links = list() // list of machines this machine is linked to
/*
Associative lazylist of the telecomms_type of linked telecomms machines and a list of said machines
eg list(telecomms_type1 = list(everything linked to us with that type), telecomms_type2 = list(everything linked to us with THAT type, etc.))
*/
var/list/links_by_telecomms_type
var/traffic = 0 // value increases as traffic increases
var/netspeed = 5 // how much traffic to lose per tick (50 gigabytes/second * netspeed)
var/list/autolinkers = list() // list of text/number values to link with
var/id = "NULL" // identification string
var/telecomms_type = null // Relevant typepath of the machine (important to use machine's base path rather than server/preset or w.e)
var/network = "NULL" // the network of the machinery
var/list/freq_listening = list() // list of frequencies to tune into: if none, will listen to all
var/integrity = 100 // basically HP, loses integrity by heat
var/produces_heat = TRUE //whether the machine will produce heat when on.
var/delay = 10 // how many process() ticks to delay per heat
var/circuitboard = null // string pointing to a circuitboard type
var/hide = FALSE // Is it a hidden machine?
// Overmap ranges in terms of map tile distance, used by receivers, relays, and broadcasters (and AIOs)
var/overmap_range = 0
///Looping sounds for any servers
// var/datum/looping_sound/server/soundloop
/obj/machinery/telecomms/Initialize(mapload)
. = ..()
// soundloop = new(list(src), on)
SSmachinery.all_telecomms += src
if(mapload)
return INITIALIZE_HINT_LATELOAD
/obj/machinery/telecomms/LateInitialize()
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
if(autolinkers.len)
for(var/obj/machinery/telecomms/T in SSmachinery.all_telecomms)
if(T != src && (T.z in GetConnectedZlevels(z)))
add_automatic_link(T)
/obj/machinery/telecomms/Destroy()
// QDEL_NULL(soundloop)
SSmachinery.all_telecomms -= src
for(var/obj/machinery/telecomms/comm in SSmachinery.all_telecomms)
remove_link(comm)
links = list()
return ..()
// This proc returns distance, so -1 is our error value
/obj/machinery/telecomms/proc/receive_range(datum/signal/subspace/sig)
if(!use_power || !istype(sig) || !is_freq_listening(sig))
return -1
return get_signal_dist(sig)
/obj/machinery/telecomms/proc/broadcast_levels(datum/signal/subspace/sig)
if(!use_power || !istype(sig) || !is_freq_listening(sig))
return
. = GetConnectedZlevels(z)
if(current_map.use_overmap && linked)
for(var/obj/effect/overmap/visitable/V in range(overmap_range, linked))
. |= V.map_z
// Used in auto linking
/obj/machinery/telecomms/proc/add_automatic_link(var/obj/machinery/telecomms/T)
if(src == T)
return
for(var/autolinker_id in autolinkers)
if(autolinker_id in T.autolinkers)
add_new_link(T)
return
/obj/machinery/telecomms/update_icon()
var/state = construct_op ? "[initial(icon_state)]_o" : initial(icon_state)
if(!use_power)
state += "_off"
icon_state = state
/obj/machinery/telecomms/process()
if(!use_power) return
if(inoperable(EMPED))
toggle_power(additional_flags = EMPED)
return
// Check heat and generate some
check_heat()
traffic = max(0, traffic - netspeed)
if(traffic > 0)
toggle_power(POWER_USE_ACTIVE)
else
toggle_power(POWER_USE_IDLE)
/obj/machinery/telecomms/emp_act(severity)
. = ..()
if(stat & EMPED || !prob(100/severity))
return
stat |= EMPED
var/duration = (300 SECONDS)/severity
spawn(duration + rand(-20, 20))
stat &= ~EMPED
/obj/machinery/telecomms/proc/check_heat()
// Checks heat from the environment and applies any integrity damage
var/datum/gas_mixture/environment = loc.return_air()
var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick
switch(environment.temperature)
if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage
damage_chance = 10
if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage
damage_chance = 25
if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage
damage_chance = 50
if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick.
damage_chance = 100
if (damage_chance && prob(damage_chance))
integrity = between(0, integrity - 1, 100)
if(delay > 0)
delay--
else if(use_power)
produce_heat()
delay = initial(delay)
/obj/machinery/telecomms/proc/produce_heat()
if (!produces_heat || !use_power || inoperable(EMPED))
return
var/turf/simulated/L = loc
if(!istype(L))
return
var/datum/gas_mixture/env = L.return_air()
var/transfer_moles = 0.25 * env.total_moles
var/datum/gas_mixture/removed = env.remove(transfer_moles)
if(!removed)
return
var/heat_produced = get_power_usage() //obviously can't produce more heat than the machine draws from it's power source
if (use_power < POWER_USE_ACTIVE)
heat_produced *= 0.30 //if idle, produce less heat.
removed.add_thermal_energy(heat_produced)
env.merge(removed)
// relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending
/obj/machinery/telecomms/proc/relay_information(datum/signal/subspace/signal, filter, copysig, amount = 20)
if(!use_power)
return
if(!filter || !ispath(filter, /obj/machinery/telecomms))
CRASH("null or non /obj/machinery/telecomms typepath given as the filter argument! given typepath: [filter]")
var/send_count = 0
if(integrity < 100)
signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity
// Loop through all linked machines and send the signal or copy.
for(var/obj/machinery/telecomms/filtered_machine in links_by_telecomms_type?[filter])
if(!filtered_machine.use_power || !(z in GetConnectedZlevels(filtered_machine.z)))
continue
if(amount && send_count >= amount)
break
send_count++
if(filtered_machine.is_freq_listening(signal))
filtered_machine.traffic++
if(copysig)
filtered_machine.receive_information(signal.copy(), src)
else
filtered_machine.receive_information(signal, src)
if(send_count > 0 && is_freq_listening(signal))
traffic++
return send_count
// send signal directly to a machine
/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine)
if(use_power)
machine.receive_information(signal, src)
// receive information from linked machinery
/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
return
/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal)
// return TRUE if found, FALSE if not found
return signal && (!freq_listening.len || (signal.frequency in freq_listening))
// Reception range of telecomms machines is limited via overmap_range
// Returns distance, not a boolean value, so don't do !get_reception or so help me god
/obj/machinery/telecomms/proc/get_signal_dist(datum/signal/subspace/signal)
if(!current_map.use_overmap || !istype(linked) || !istype(signal.sector))
if(z == signal.origin_level || (signal.origin_level in GetConnectedZlevels(z)))
return 1
else
return -1
if (signal.sector == linked)
return 0
var/overmap_dist = get_dist(signal.sector, linked)
if (overmap_dist > overmap_range)
return -1
return overmap_dist

View File

@@ -1,648 +0,0 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32
/*
Hello, friends, this is Doohl from sexylands. You may be wondering what this
monstrous code file is. Sit down, boys and girls, while I tell you the tale.
The machines defined in this file were designed to be compatible with any radio
signals, provided they use subspace transmission. Currently they are only used for
headsets, but they can eventually be outfitted for real COMPUTER networks. This
is just a skeleton, ladies and gentlemen.
Look at radio.dm for the prequel to this code.
*/
var/global/list/obj/machinery/telecomms/telecomms_list = list()
/obj/machinery/telecomms
icon = 'icons/obj/machines/telecomms.dmi'
var/list/links = list() // list of machines this machine is linked to
var/traffic = 0 // value increases as traffic increases
var/netspeed = 5 // how much traffic to lose per tick (50 gigabytes/second * netspeed)
var/list/autolinkers = list() // list of text/number values to link with
var/id = "NULL" // identification string
var/network = "NULL" // the network of the machinery
var/list/freq_listening = list() // list of frequencies to tune into: if none, will listen to all
var/machinetype = 0 // just a hacky way of preventing alike machines from pairing
var/toggled = 1 // Is it toggled on
var/on = 1
var/integrity = 100 // basically HP, loses integrity by heat
var/produces_heat = 1 //whether the machine will produce heat when on.
var/delay = 10 // how many process() ticks to delay per heat
var/long_range_link = 0 // Can you link it across Z levels or on the otherside of the map? (Relay & Hub)
var/circuitboard = null // string pointing to a circuitboard type
var/hide = 0 // Is it a hidden machine?
var/list/listening_level = 0 // 0 = auto set in New() - this is the z level that the machine is listening to.
var/overmap_range = 2 //OVERMAP: Number of sectors out we can communicate
///Looping sounds for any servers
// var/datum/looping_sound/server/soundloop
/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20)
// relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending
if(!on)
return
var/send_count = 0
signal.data["slow"] += rand(0, round((100-integrity))) // apply some lag based on integrity
// Loop through all linked machines and send the signal or copy.
for(var/obj/machinery/telecomms/machine in links)
if(filter && !istype( machine, text2path(filter) ))
continue
if(!machine.on)
continue
if(amount && send_count >= amount)
break
if(!(machine.loc.z in listening_level))
if(long_range_link == 0 && machine.long_range_link == 0)
continue
// If we're sending a copy, be sure to create the copy for EACH machine and paste the data
var/datum/signal/copy
if(copysig)
copy = new
copy.transmission_method = TRANSMISSION_SUBSPACE
copy.frequency = signal.frequency
copy.data = signal.data.Copy()
// Keep the "original" signal constant
if(!signal.data["original"])
copy.data["original"] = signal
else
copy.data["original"] = signal.data["original"]
send_count++
if(machine.is_freq_listening(signal))
machine.traffic++
if(copysig && copy)
machine.receive_information(copy, src)
else
machine.receive_information(signal, src)
if(send_count > 0 && is_freq_listening(signal))
traffic++
return send_count
/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine)
// send signal directly to a machine
machine.receive_information(signal, src)
/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
// receive information from linked machinery
return
/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal)
// return 1 if found, 0 if not found
if(!signal)
return 0
if((signal.frequency in freq_listening) || (!freq_listening.len))
return 1
else
return 0
//OVERMAP: Since telecomms is subspace, limit how far it goes. This prevents double-broadcasts across the entire overmap, and gives the ability to intrude on comms range of other ships
/obj/machinery/telecomms/proc/check_receive_sector(datum/signal/signal)
if(isAdminLevel(z) || isAdminLevel(signal.data["level"])) //Messages to and from centcomm levels are not sector-restricted.
return TRUE
if(current_map.use_overmap)
if(!linked) //If we're using overmap and not associated with a sector, doesn't work.
return FALSE
var/obj/effect/overmap/visitable/S = signal.data["sector"]
if(istype(S)) //If our signal isn't sending a sector, it's something associated with telecomms_process_active(), which has their own limits.
if(S != linked) //If we're not the same ship, check range
if(get_dist(S, linked) > overmap_range && !(S in view(overmap_range, linked)))
return FALSE
return TRUE
/obj/machinery/telecomms/New()
telecomms_list += src
..()
/obj/machinery/telecomms/Initialize()
. = ..()
// soundloop = new(list(src), on)
if(autolinkers.len)
// Links nearby machines
if(!long_range_link)
for(var/obj/machinery/telecomms/T in orange(20, src))
add_link(T)
else
for(var/obj/machinery/telecomms/T in telecomms_list)
add_link(T)
if(!listening_level)
listening_level = list(z)
var/turf/above = GetAbove(src)
var/turf/below = GetBelow(src)
if(above)
listening_level += above.z
if(below)
listening_level += below.z
if(current_map.use_overmap && !linked)
var/my_sector = map_sectors["[z]"]
if (istype(my_sector, /obj/effect/overmap/visitable))
attempt_hook_up(my_sector)
/obj/machinery/telecomms/Destroy()
// QDEL_NULL(soundloop)
telecomms_list -= src
for(var/obj/machinery/telecomms/comm in telecomms_list)
comm.links -= src
links = list()
return ..()
// Used in auto linking
/obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T)
var/turf/position = get_turf(src)
var/turf/T_position = get_turf(T)
if((position.z == T_position.z) || (src.long_range_link && T.long_range_link))
for(var/x in autolinkers)
if(T.autolinkers.Find(x))
if(src != T)
links |= T
/obj/machinery/telecomms/update_icon()
var/state = construct_op ? "[initial(icon_state)]_o" : initial(icon_state)
if(!on)
state += "_off"
icon_state = state
/obj/machinery/telecomms/proc/update_power()
if(toggled)
if(stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off
on = FALSE
// soundloop.stop(src)
else
on = TRUE
// soundloop.start(src)
else
on = FALSE
// soundloop.stop(src)
/obj/machinery/telecomms/process()
update_power()
// Check heat and generate some
checkheat()
// Update the icon
update_icon()
if(traffic > 0)
traffic -= netspeed
/obj/machinery/telecomms/emp_act(severity)
if(prob(100/severity))
if(!(stat & EMPED))
stat |= EMPED
var/duration = (300 * 10)/severity
spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot.
stat &= ~EMPED
..()
/obj/machinery/telecomms/proc/checkheat()
// Checks heat from the environment and applies any integrity damage
var/datum/gas_mixture/environment = loc.return_air()
var/damage_chance = 0 // Percent based chance of applying 1 integrity damage this tick
switch(environment.temperature)
if((T0C + 40) to (T0C + 70)) // 40C-70C, minor overheat, 10% chance of taking damage
damage_chance = 10
if((T0C + 70) to (T0C + 130)) // 70C-130C, major overheat, 25% chance of taking damage
damage_chance = 25
if((T0C + 130) to (T0C + 200)) // 130C-200C, dangerous overheat, 50% chance of taking damage
damage_chance = 50
if((T0C + 200) to INFINITY) // More than 200C, INFERNO. Takes damage every tick.
damage_chance = 100
if (damage_chance && prob(damage_chance))
integrity = between(0, integrity - 1, 100)
if(delay > 0)
delay--
else if(on)
produce_heat()
delay = initial(delay)
/obj/machinery/telecomms/proc/produce_heat()
if (!produces_heat)
return
if (!use_power)
return
if(!(stat & (NOPOWER|BROKEN)))
var/turf/simulated/L = loc
if(istype(L))
var/datum/gas_mixture/env = L.return_air()
var/transfer_moles = 0.25 * env.total_moles
var/datum/gas_mixture/removed = env.remove(transfer_moles)
if(removed)
var/heat_produced = idle_power_usage //obviously can't produce more heat than the machine draws from it's power source
if (traffic <= 0)
heat_produced *= 0.30 //if idle, produce less heat.
removed.add_thermal_energy(heat_produced)
env.merge(removed)
/*
The receiver idles and receives messages from subspace-compatible radio equipment;
primarily headsets. They then just relay this information to all linked devices,
which can would probably be network hubs.
Link to Processor Units in case receiver can't send to bus units.
*/
/obj/machinery/telecomms/receiver
name = "Subspace Receiver"
icon_state = "broadcast receiver"
desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity."
density = 1
anchored = 1
idle_power_usage = 600
machinetype = 1
produces_heat = 0
circuitboard = "/obj/item/circuitboard/telecomms/receiver"
/obj/machinery/telecomms/receiver/receive_signal(datum/signal/signal)
if(!on) // has to be on to receive messages
return
if(!signal)
return
if(!check_receive_level(signal))
return
if(!check_receive_sector(signal))
return
if(signal.transmission_method == TRANSMISSION_SUBSPACE)
if(is_freq_listening(signal)) // detect subspace signals
//Remove the level and then start adding levels that it is being broadcasted in.
signal.data["level"] = list()
var/can_send = relay_information(signal, "/obj/machinery/telecomms/hub") // ideally relay the copied information to relays
if(!can_send)
relay_information(signal, "/obj/machinery/telecomms/bus") // Send it to a bus instead, if it's linked to one
/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/signal)
if(!(signal.data["level"] in listening_level))
for(var/obj/machinery/telecomms/hub/H in links)
var/list/connected_levels = list()
for(var/obj/machinery/telecomms/relay/R in H.links)
if(R.can_receive(signal))
connected_levels |= R.listening_level
if(signal.data["level"] in connected_levels)
return 1
return 0
return 1
/*
The HUB idles until it receives information. It then passes on that information
depending on where it came from.
This is the heart of the Telecommunications Network, sending information where it
is needed. It mainly receives information from long-distance Relays and then sends
that information to be processed. Afterwards it gets the uncompressed information
from Servers/Buses and sends that back to the relay, to then be broadcasted.
*/
/obj/machinery/telecomms/hub
name = "Telecommunication Hub"
icon_state = "hub"
desc = "A mighty piece of hardware used to send/receive massive amounts of data."
density = 1
anchored = 1
idle_power_usage = 1600
machinetype = 7
circuitboard = "/obj/item/circuitboard/telecomms/hub"
long_range_link = 1
netspeed = 40
/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
if(is_freq_listening(signal))
if(istype(machine_from, /obj/machinery/telecomms/receiver))
//If the signal is compressed, send it to the bus.
relay_information(signal, "/obj/machinery/telecomms/bus", 1) // ideally relay the copied information to bus units
else
// Get a list of relays that we're linked to, then send the signal to their levels.
relay_information(signal, "/obj/machinery/telecomms/relay", 1)
relay_information(signal, "/obj/machinery/telecomms/broadcaster", 1) // Send it to a broadcaster.
/*
The relay idles until it receives information. It then passes on that information
depending on where it came from.
The relay is needed in order to send information pass Z levels. It must be linked
with a HUB, the only other machine that can send/receive pass Z levels.
*/
/obj/machinery/telecomms/relay
name = "Telecommunication Relay"
icon_state = "relay"
desc = "A mighty piece of hardware used to send massive amounts of data far away."
density = 1
anchored = 1
idle_power_usage = 600
machinetype = 8
produces_heat = 0
circuitboard = "/obj/item/circuitboard/telecomms/relay"
netspeed = 5
long_range_link = 1
var/broadcasting = TRUE
var/receiving = 1
/obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
// Add our level and send it back
if(can_send(signal))
signal.data["level"] |= listening_level
// Checks to see if it can send/receive.
/obj/machinery/telecomms/relay/proc/can(datum/signal/signal)
if(!on)
return 0
if(!is_freq_listening(signal))
return 0
return 1
/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal)
if(!can(signal))
return 0
return broadcasting
/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal)
if(!can(signal))
return 0
return receiving
/*
The bus mainframe idles and waits for hubs to relay them signals. They act
as junctions for the network.
They transfer uncompressed subspace packets to processor units, and then take
the processed packet to a server for logging.
Link to a subspace hub if it can't send to a server.
*/
/obj/machinery/telecomms/bus
name = "Bus Mainframe"
icon_state = "bus"
desc = "A mighty piece of hardware used to send massive amounts of data quickly."
density = 1
anchored = 1
idle_power_usage = 1000
machinetype = 2
circuitboard = "/obj/item/circuitboard/telecomms/bus"
netspeed = 40
var/change_frequency = 0
/obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
if(is_freq_listening(signal))
if(change_frequency)
signal.frequency = change_frequency
if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it
// send to one linked processor unit
var/send_to_processor = relay_information(signal, "/obj/machinery/telecomms/processor")
if(send_to_processor)
return
// failed to send to a processor, relay information anyway
signal.data["slow"] += rand(1, 5) // slow the signal down only slightly
src.receive_information(signal, src)
// Try sending it!
var/list/try_send = list("/obj/machinery/telecomms/server", "/obj/machinery/telecomms/hub", "/obj/machinery/telecomms/broadcaster", "/obj/machinery/telecomms/bus")
var/i = 0
for(var/send in try_send)
if(i)
signal.data["slow"] += rand(0, 1) // slow the signal down only slightly
i++
var/can_send = relay_information(signal, send)
if(can_send)
break
/*
The processor is a very simple machine that decompresses subspace signals and
transfers them back to the original bus. It is essential in producing audible
data.
Link to servers if bus is not present
*/
/obj/machinery/telecomms/processor
name = "Processor Unit"
icon_state = "processor"
desc = "This machine is used to process large quantities of information."
density = 1
anchored = 1
idle_power_usage = 600
machinetype = 3
delay = 5
circuitboard = "/obj/item/circuitboard/telecomms/processor"
var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals
receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
if(is_freq_listening(signal))
if(process_mode)
signal.data["compression"] = 0 // uncompress subspace signal
else
signal.data["compression"] = 100 // even more compressed signal
if(istype(machine_from, /obj/machinery/telecomms/bus))
relay_direct_information(signal, machine_from) // send the signal back to the machine
else // no bus detected - send the signal to servers instead
signal.data["slow"] += rand(5, 10) // slow the signal down
relay_information(signal, "/obj/machinery/telecomms/server")
/*
The server logs all traffic and signal data. Once it records the signal, it sends
it to the subspace broadcaster.
Store a maximum of 100 logs and then deletes them.
*/
/obj/machinery/telecomms/server
name = "Telecommunication Server"
icon_state = "comm_server"
desc = "A machine used to store data and network statistics."
density = 1
anchored = 1
idle_power_usage = 300
machinetype = 4
circuitboard = "/obj/item/circuitboard/telecomms/server"
var/list/log_entries = list()
var/list/stored_names = list()
var/list/TrafficActions = list()
var/logs = 0 // number of logs
var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes)
var/list/memory = list() // stored memory
var/rawcode = "" // the code to compile (raw text)
var/datum/ntsl2_program/tcomm/Program // NTSL2++ datum responsible for script execution
var/autoruncode = 0 // 1 if the code is set to run every time a signal is picked up
var/encryption = "null" // encryption key: ie "password"
var/salt = "null" // encryption salt: ie "123comsat"
// would add up to md5("password123comsat")
var/language = "human"
var/obj/item/device/radio/headset/server_radio = null
/obj/machinery/telecomms/server/Initialize()
. = ..()
Program = SSntsl2.new_program_tcomm(src)
server_radio = new()
/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
if(signal.data["message"])
if(is_freq_listening(signal))
if(traffic > 0)
totaltraffic += traffic // add current traffic to total traffic
//Is this a test signal? Bypass logging
if(signal.data["type"] != 4)
// If signal has a message and appropriate frequency
update_logs()
var/datum/comm_log_entry/log = new
var/mob/M = signal.data["mob"]
// Copy the signal.data entries we want
log.parameters["mobtype"] = signal.data["mobtype"]
log.parameters["job"] = signal.data["job"]
log.parameters["key"] = signal.data["key"]
log.parameters["vmessage"] = signal.data["message"]
log.parameters["vname"] = signal.data["vname"]
log.parameters["message"] = signal.data["message"]
log.parameters["name"] = signal.data["name"]
log.parameters["realname"] = signal.data["realname"]
log.parameters["language"] = signal.data["language"]
var/race = "Unknown"
if(ishuman(M) || isbrain(M))
race = "Sapient Species"
log.parameters["intelligible"] = 1
else if(M.isMonkey())
race = "Monkey"
else if(issilicon(M))
race = "Artificial Life"
log.parameters["intelligible"] = 1
else if(isslime(M))
race = "Slime"
else if(isanimal(M))
race = "Domestic Animal"
else if(istype(M, /mob/living/announcer))
race = "Announcer"
log.parameters["race"] = race
if(!istype(M, /mob/abstract/new_player) && M)
log.parameters["uspeech"] = M.universal_speak
else
log.parameters["uspeech"] = 0
// If the signal is still compressed, make the log entry gibberish
if(signal.data["compression"] > 0)
log.parameters["message"] = Gibberish(signal.data["message"], signal.data["compression"] + 50)
log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50)
log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50)
log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50)
log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50)
log.input_type = "Corrupt File"
// Log and store everything that needs to be logged
log_entries.Add(log)
if(!(signal.data["name"] in stored_names))
stored_names.Add(signal.data["name"])
logs++
signal.data["server"] = src
// Give the log a name
var/identifier = num2text( rand(-1000,1000) + world.time )
log.name = "data packet ([md5(identifier)])"
if(istype(Program))
Program.process_message(signal, CALLBACK(src, .proc/program_receive_information, signal))
finish_receive_information(signal)
/obj/machinery/telecomms/server/proc/program_receive_information(datum/signal/signal)
Program.retrieve_messages(CALLBACK(src, .proc/finish_receive_information, signal))
/obj/machinery/telecomms/server/proc/finish_receive_information(datum/signal/signal)
var/can_send = relay_information(signal, "/obj/machinery/telecomms/hub")
if(!can_send)
relay_information(signal, "/obj/machinery/telecomms/broadcaster")
/obj/machinery/telecomms/server/process()
. = ..()
if(istype(Program))
Program.retrieve_messages()
/obj/machinery/telecomms/server/proc/update_logs()
// start deleting the very first log entry
if(logs >= 400)
for(var/i = 1, i <= logs, i++) // locate the first garbage collectable log entry and remove it
var/datum/comm_log_entry/L = log_entries[i]
if(L.garbage_collector)
log_entries.Remove(L)
logs--
break
/obj/machinery/telecomms/server/proc/add_entry(var/content, var/input)
var/datum/comm_log_entry/log = new
var/identifier = num2text( rand(-1000,1000) + world.time )
log.name = "[input] ([md5(identifier)])"
log.input_type = input
log.parameters["message"] = content
log_entries.Add(log)
update_logs()
// Simple log entry datum
/datum/comm_log_entry
var/parameters = list() // carbon-copy to signal.data[]
var/name = "data packet (#)"
var/garbage_collector = 1 // if set to 0, will not be garbage collected
var/input_type = "Speech File"
// NTSL2++ code

View File

@@ -1,161 +0,0 @@
/*
Telecomms monitor tracks the overall trafficing of a telecommunications network
and displays a heirarchy of linked machines.
*/
/obj/machinery/computer/telecomms/monitor
name = "Telecommunications Monitor"
desc = "A monitor that tracks the overall traffic of a telecommunicaations network, and displays a hierarchy of linked machines."
icon_screen = "comm_monitor"
icon_keyboard = "green_key"
light_color = LIGHT_COLOR_GREEN
var/screen = 0 // the screen number:
var/list/machinelist = list() // the machines located by the computer
var/obj/machinery/telecomms/SelectedMachine
var/network = "NULL" // the network to probe
var/temp = "" // temporary feedback messages
attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
user.set_machine(src)
var/dat = "<TITLE>Telecommunications Monitor</TITLE><center><b>Telecommunications Monitor</b></center>"
switch(screen)
// --- Main Menu ---
if(0)
dat += "<br>[temp]<br><br>"
dat += "<br>Current Network: <a href='?src=\ref[src];network=1'>[network]</a><br>"
if(machinelist.len)
dat += "<br>Detected Network Entities:<ul>"
for(var/obj/machinery/telecomms/T in machinelist)
dat += "<li><a href='?src=\ref[src];viewmachine=[T.id]'>\ref[T] [T.name]</a> ([T.id])</li>"
dat += "</ul>"
dat += "<br><a href='?src=\ref[src];operation=release'>\[Flush Buffer\]</a>"
else
dat += "<a href='?src=\ref[src];operation=probe'>\[Probe Network\]</a>"
// --- Viewing Machine ---
if(1)
dat += "<br>[temp]<br>"
dat += "<center><a href='?src=\ref[src];operation=mainmenu'>\[Main Menu\]</a></center>"
dat += "<br>Current Network: [network]<br>"
dat += "Selected Network Entity: [SelectedMachine.name] ([SelectedMachine.id])<br>"
dat += "Linked Entities: <ol>"
for(var/obj/machinery/telecomms/T in SelectedMachine.links)
if(!T.hide)
dat += "<li><a href='?src=\ref[src];viewmachine=[T.id]'>\ref[T.id] [T.name]</a> ([T.id])</li>"
dat += "</ol>"
user << browse(dat, "window=comm_monitor;size=575x400")
onclose(user, "server_control")
temp = ""
return
Topic(href, href_list)
if(..())
return
add_fingerprint(usr)
usr.set_machine(src)
if(href_list["viewmachine"])
screen = 1
for(var/obj/machinery/telecomms/T in machinelist)
if(T.id == href_list["viewmachine"])
SelectedMachine = T
break
if(href_list["operation"])
switch(href_list["operation"])
if("release")
machinelist = list()
screen = 0
if("mainmenu")
screen = 0
if("probe")
if(machinelist.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font>"
else
for(var/obj/machinery/telecomms/T in range(25, src))
if(T.network == network)
machinelist.Add(T)
if(!machinelist.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\] -</font>"
else
temp = "<font color = #336699>- [machinelist.len] ENTITIES LOCATED & BUFFERED -</font>"
screen = 0
if(href_list["network"])
var/newnet = sanitize(input(usr, "Which network do you want to view?", "Comm Monitor", network) as null|text)
if(newnet && ((usr in range(1, src) || issilicon(usr))))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font>"
else
network = newnet
screen = 0
machinelist = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font>"
updateUsrDialog()
return
attackby(var/obj/item/D as obj, var/mob/user as mob)
if(D.isscrewdriver())
if(D.use_tool(src, user, 20, volume = 50))
if (src.stat & BROKEN)
to_chat(user, "<span class='notice'>The broken glass falls out.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
new /obj/item/material/shard( src.loc )
var/obj/item/circuitboard/comm_monitor/M = new /obj/item/circuitboard/comm_monitor( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 3
A.icon_state = "3"
A.anchored = 1
qdel(src)
else
to_chat(user, "<span class='notice'>You disconnect the monitor.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
var/obj/item/circuitboard/comm_monitor/M = new /obj/item/circuitboard/comm_monitor( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 4
A.icon_state = "4"
A.anchored = 1
qdel(src)
src.updateUsrDialog()
return
/obj/machinery/computer/telecomms/monitor/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, "<span class='notice'>You you disable the security protocols</span>")
src.updateUsrDialog()
return 1

View File

@@ -1,246 +0,0 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32
/obj/machinery/computer/telecomms/traffic
name = "Telecommunications Traffic Control"
icon_screen = "computer_generic"
icon_keyboard = "green_key"
light_color = LIGHT_COLOR_GREEN
var/screen = 0 // the screen number:
var/list/servers = list() // the servers located by the computer
var/mob/editingcode
var/mob/lasteditor
var/list/viewingcode = list()
var/obj/machinery/telecomms/server/SelectedServer
var/network = "NULL" // the network to probe
var/temp = "" // temporary feedback messages
var/storedcode = "" // code stored
proc/update_ide()
// loop if there's someone manning the keyboard
while(editingcode)
if(!editingcode.client)
editingcode = null
break
// For the typer, the input is enabled. Buffer the typed text
if(editingcode)
storedcode = "[winget(editingcode, "tcscode", "text")]"
if(editingcode) // double if's to work around a runtime error
winset(editingcode, "tcscode", "is-disabled=false")
// If the player's not manning the keyboard anymore, adjust everything
if( (!(editingcode in range(1, src)) && !issilicon(editingcode)) || (editingcode.machine != src && !issilicon(editingcode)))
if(editingcode)
winshow(editingcode, "Telecomms IDE", 0) // hide the window!
editingcode = null
break
// For other people viewing the typer type code, the input is disabled and they can only view the code
// (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once)
if(length(viewingcode))
// This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
for(var/mob/M in viewingcode)
if((M.machine == src && (M in view(1, src))) || issilicon(M))
winset(M, "tcscode", "is-disabled=true")
winset(M, "tcscode", "text=\"[showcode]\"")
else
viewingcode.Remove(M)
winshow(M, "Telecomms IDE", 0) // hide the window!
sleep(5)
if(length(viewingcode) > 0)
editingcode = pick(viewingcode)
viewingcode.Remove(editingcode)
update_ide()
req_access = list(access_tcomsat)
attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
user.set_machine(src)
var/dat = "<TITLE>Telecommunication Traffic Control</TITLE><center><b>Telecommunications Traffic Control</b></center>"
switch(screen)
// --- Main Menu ---
if(0)
dat += "<br>[temp]<br>"
dat += "<br>Current Network: <a href='?src=\ref[src];network=1'>[network]</a><br>"
if(servers.len)
dat += "<br>Detected Telecommunication Servers:<ul>"
for(var/obj/machinery/telecomms/T in servers)
dat += "<li><a href='?src=\ref[src];viewserver=[T.id]'>\ref[T] [T.name]</a> ([T.id])</li>"
dat += "</ul>"
dat += "<br><a href='?src=\ref[src];operation=release'>\[Flush Buffer\]</a>"
else
dat += "<br>No servers detected. Scan for servers: <a href='?src=\ref[src];operation=scan'>\[Scan\]</a>"
// --- Viewing Server ---
if(1)
dat += "<br>[temp]<br>"
dat += "<center><a href='?src=\ref[src];operation=mainmenu'>\[Main Menu\]</a> <a href='?src=\ref[src];operation=refresh'>\[Refresh\]</a></center>"
dat += "<br>Current Network: [network]"
dat += "<br>Selected Server: [SelectedServer.id]<br><br>"
dat += "<br><a href='?src=\ref[src];operation=editcode'>\[Edit Code\]</a>"
dat += "<br>Signal Execution: "
if(SelectedServer.autoruncode)
dat += "<a href='?src=\ref[src];operation=togglerun'>ALWAYS</a>"
else
dat += "<a href='?src=\ref[src];operation=togglerun'>NEVER</a>"
user << browse(dat, "window=traffic_control;size=575x400")
onclose(user, "server_control")
temp = ""
return
Topic(href, href_list)
if(..())
return
add_fingerprint(usr)
usr.set_machine(src)
if(!src.allowed(usr) && !emagged)
to_chat(usr, "<span class='warning'>ACCESS DENIED.</span>")
return
if(href_list["viewserver"])
screen = 1
for(var/obj/machinery/telecomms/T in servers)
if(T.id == href_list["viewserver"])
SelectedServer = T
break
if(href_list["operation"])
switch(href_list["operation"])
if("release")
servers = list()
screen = 0
if("mainmenu")
screen = 0
if("scan")
if(servers.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font>"
else
for(var/obj/machinery/telecomms/server/T in range(25, src))
if(T.network == network)
servers.Add(T)
if(!servers.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -</font>"
else
temp = "<font color = #336699>- [servers.len] SERVERS PROBED & BUFFERED -</font>"
screen = 0
if("editcode")
if(editingcode == usr) return
if(usr in viewingcode) return
if(!editingcode)
lasteditor = usr
editingcode = usr
winshow(editingcode, "Telecomms IDE", 1) // show the IDE
winset(editingcode, "tcscode", "is-disabled=false")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
winset(editingcode, "tcscode", "text=\"[showcode]\"")
spawn()
update_ide()
else
viewingcode.Add(usr)
winshow(usr, "Telecomms IDE", 1) // show the IDE
winset(usr, "tcscode", "is-disabled=true")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\"", "\\\"")
winset(usr, "tcscode", "text=\"[showcode]\"")
if("togglerun")
SelectedServer.autoruncode = !(SelectedServer.autoruncode)
if(href_list["network"])
var/newnet = sanitize(input(usr, "Which network do you want to view?", "Comm Monitor", network) as null|text)
if(newnet && ((usr in range(1, src)) || issilicon(usr)))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font>"
else
network = newnet
screen = 0
servers = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font>"
updateUsrDialog()
return
attackby(var/obj/item/D as obj, var/mob/user as mob)
if(D.isscrewdriver())
if(D.use_tool(src, user, 20, volume = 50))
if (src.stat & BROKEN)
to_chat(user, "<span class='notice'>The broken glass falls out.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
new /obj/item/material/shard( src.loc )
var/obj/item/circuitboard/comm_traffic/M = new /obj/item/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 3
A.icon_state = "3"
A.anchored = 1
qdel(src)
else
to_chat(user, "<span class='notice'>You disconnect the monitor.</span>")
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
var/obj/item/circuitboard/comm_traffic/M = new /obj/item/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.forceMove(src.loc)
A.circuit = M
A.state = 4
A.icon_state = "4"
A.anchored = 1
qdel(src)
src.updateUsrDialog()
return
/obj/machinery/computer/telecomms/traffic/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
to_chat(user, "<span class='notice'>You you disable the security protocols</span>")
src.updateUsrDialog()
return 1

View File

@@ -280,13 +280,13 @@
<table class="request">
<tr>
<td class="radio">Transmit:</td>
<td><a href='byond://?src=\ref[src];wires=4'>[radio.broadcasting ? "<font color=#55FF55>En" : "<font color=#FF5555>Dis" ]abled</font></a>
<td><a href='byond://?src=\ref[src];wires=4'>[radio.get_broadcasting() ? "<font color=#55FF55>En" : "<font color=#FF5555>Dis" ]abled</font></a>
</td>
</tr>
<tr>
<td class="radio">Receive:</td>
<td><a href='byond://?src=\ref[src];wires=2'>[radio.listening ? "<font color=#55FF55>En" : "<font color=#FF5555>Dis" ]abled</font></a>
<td><a href='byond://?src=\ref[src];wires=2'>[radio.get_listening() ? "<font color=#55FF55>En" : "<font color=#FF5555>Dis" ]abled</font></a>
</td>
</tr>
@@ -371,9 +371,9 @@
var/t1 = text2num(href_list["wires"])
switch(t1)
if(4)
radio.ToggleBroadcast()
radio.set_broadcasting(!radio.get_broadcasting())
if(2)
radio.ToggleReception()
radio.set_listening(!radio.get_listening())
if(href_list["setlaws"])
var/newlaws = sanitize(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws) as message)
if(newlaws)

View File

@@ -1,5 +1,6 @@
#define CHANNEL_COMMON "Common"
#define CHANNEL_ENTERTAINMENT "Entertainment"
#define CHANNEL_HAILING "Hailing"
#define CHANNEL_COMMAND "Command"
#define CHANNEL_SCIENCE "Science"
#define CHANNEL_MEDICAL "Medical"
@@ -17,11 +18,11 @@
#define CHANNEL_NINJA "Ninja"
#define CHANNEL_BLUESPACE "Bluespace"
#define CHANNEL_BURGLAR "Burglar"
#define CHANNEL_SHIP "Ship"
var/global/list/ALL_RADIO_CHANNELS = list(
CHANNEL_COMMON = TRUE,
CHANNEL_ENTERTAINMENT = TRUE,
CHANNEL_HAILING = TRUE,
CHANNEL_COMMAND = TRUE,
CHANNEL_SCIENCE = TRUE,
CHANNEL_MEDICAL = TRUE,
@@ -36,6 +37,5 @@ var/global/list/ALL_RADIO_CHANNELS = list(
CHANNEL_MERCENARY = TRUE,
CHANNEL_NINJA = TRUE,
CHANNEL_BLUESPACE = TRUE,
CHANNEL_BURGLAR = TRUE,
CHANNEL_SHIP = TRUE
CHANNEL_BURGLAR = TRUE
)

View File

@@ -3,7 +3,6 @@
desc = "Dance my monkeys! DANCE!!!"
icon_state = "electropack0"
item_state = "electropack"
frequency = 1449
flags = CONDUCT
slot_flags = SLOT_BACK
w_class = ITEMSIZE_HUGE
@@ -12,6 +11,10 @@
var/code = 2
/obj/item/device/radio/electropack/Initialize()
. = ..()
set_frequency(1449)
/obj/item/device/radio/electropack/attack_hand(mob/user as mob)
if(src == user.back)
to_chat(user, "<span class='notice'>You need help taking this off!</span>")

View File

@@ -9,34 +9,60 @@
var/translate_binary = FALSE
var/translate_hivenet = FALSE
var/syndie = FALSE // Signifies that it de-crypts Syndicate transmissions
var/independent = FALSE // Signifies that it lets you talk on the spicy channel
var/list/channels = list(CHANNEL_COMMON = TRUE, CHANNEL_ENTERTAINMENT = TRUE)
var/list/additional_channels = list()
/obj/item/device/encryptionkey/attackby(obj/item/W, mob/user)
return
/obj/item/device/encryptionkey/ship
var/use_common = FALSE
channels = list()
/obj/item/device/encryptionkey/ship/Initialize()
if(!current_map.use_overmap)
return ..()
var/turf/T = get_turf(src)
var/obj/effect/overmap/visitable/V = map_sectors["[T.z]"]
if(istype(V) && V.comms_support)
if(V.comms_name)
name = "[V.comms_name] encryption key"
channels += list(
"[V.name]" = TRUE,
CHANNEL_HAILING = TRUE
)
if(use_common)
channels += list(CHANNEL_COMMON, TRUE)
/obj/item/device/encryptionkey/ship/common
use_common = TRUE
/obj/item/device/encryptionkey/syndicate
icon_state = "cypherkey"
additional_channels = list(CHANNEL_MERCENARY = TRUE)
additional_channels = list(CHANNEL_MERCENARY = TRUE, CHANNEL_HAILING = TRUE)
origin_tech = list(TECH_ILLEGAL = 3)
desc_antag = "An encryption key that allows you to intercept comms and speak on private non-station channels. Use :t to access the private channel."
syndie = TRUE
/obj/item/device/encryptionkey/raider
icon_state = "cypherkey"
additional_channels = list(CHANNEL_RAIDER = TRUE)
additional_channels = list(CHANNEL_RAIDER = TRUE, CHANNEL_HAILING = TRUE)
origin_tech = list(TECH_ILLEGAL = 2)
syndie = TRUE
/obj/item/device/encryptionkey/burglar
icon_state = "cypherkey"
additional_channels = list(CHANNEL_BURGLAR = TRUE)
additional_channels = list(CHANNEL_BURGLAR = TRUE, CHANNEL_HAILING = TRUE)
origin_tech = list(TECH_ILLEGAL = 2)
syndie = TRUE
/obj/item/device/encryptionkey/ninja
icon_state = "cypherkey"
additional_channels = list(CHANNEL_NINJA = TRUE)
additional_channels = list(CHANNEL_NINJA = TRUE, CHANNEL_HAILING = TRUE)
origin_tech = list(TECH_ILLEGAL = 3)
syndie = TRUE
@@ -48,10 +74,6 @@
origin_tech = list(TECH_BLUESPACE = 3)
syndie = TRUE
/obj/item/device/encryptionkey/ship
icon_state = "cypherkey"
additional_channels = list(CHANNEL_SHIP = TRUE)
/obj/item/device/encryptionkey/binary
icon_state = "cypherkey"
translate_binary = TRUE
@@ -107,43 +129,43 @@
/obj/item/device/encryptionkey/headset_com
name = "command radio encryption key"
icon_state = "com_cypherkey"
channels = list(CHANNEL_COMMAND = TRUE)
channels = list(CHANNEL_COMMAND = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/heads/captain
name = "captain's encryption key"
icon_state = "cap_cypherkey"
channels = list(CHANNEL_COMMAND = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_PENAL = TRUE, CHANNEL_ENGINEERING = TRUE, CHANNEL_SCIENCE = TRUE, CHANNEL_MEDICAL = TRUE, CHANNEL_SUPPLY = TRUE, CHANNEL_SERVICE = TRUE)
channels = list(CHANNEL_COMMAND = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_PENAL = TRUE, CHANNEL_ENGINEERING = TRUE, CHANNEL_SCIENCE = TRUE, CHANNEL_MEDICAL = TRUE, CHANNEL_SUPPLY = TRUE, CHANNEL_SERVICE = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/heads/ai_integrated
name = "ai integrated encryption key"
desc = "Integrated encryption key"
icon_state = "cap_cypherkey"
channels = list(CHANNEL_COMMAND = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_PENAL = TRUE, CHANNEL_ENGINEERING = TRUE, CHANNEL_SCIENCE = TRUE, CHANNEL_MEDICAL = TRUE, CHANNEL_SUPPLY = TRUE, CHANNEL_SERVICE = TRUE, CHANNEL_AI_PRIVATE = TRUE)
channels = list(CHANNEL_COMMAND = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_PENAL = TRUE, CHANNEL_ENGINEERING = TRUE, CHANNEL_SCIENCE = TRUE, CHANNEL_MEDICAL = TRUE, CHANNEL_SUPPLY = TRUE, CHANNEL_SERVICE = TRUE, CHANNEL_AI_PRIVATE = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/heads/rd
name = "research director's encryption key"
icon_state = "rd_cypherkey"
channels = list(CHANNEL_SCIENCE = TRUE, CHANNEL_COMMAND = TRUE)
channels = list(CHANNEL_SCIENCE = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/heads/hos
name = "head of security's encryption key"
icon_state = "hos_cypherkey"
channels = list(CHANNEL_SECURITY = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_PENAL = TRUE)
channels = list(CHANNEL_SECURITY = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_PENAL = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/heads/ce
name = "chief engineer's encryption key"
icon_state = "ce_cypherkey"
channels = list(CHANNEL_ENGINEERING = TRUE, CHANNEL_COMMAND = TRUE)
channels = list(CHANNEL_ENGINEERING = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/heads/cmo
name = "chief medical officer's encryption key"
icon_state = "cmo_cypherkey"
channels = list(CHANNEL_MEDICAL = TRUE, CHANNEL_COMMAND = TRUE)
channels = list(CHANNEL_MEDICAL = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/heads/xo
name = "executive officer's encryption key"
icon_state = "hop_cypherkey"
channels = list(CHANNEL_SERVICE = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_PENAL = TRUE)
channels = list(CHANNEL_SERVICE = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_PENAL = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/headset_cargo
name = "operations radio encryption key"
@@ -153,7 +175,7 @@
/obj/item/device/encryptionkey/headset_operations_manager
name = "operations managaer radio encryption key"
icon_state = "cargo_cypherkey"
channels = list(CHANNEL_COMMAND = TRUE, CHANNEL_SUPPLY = TRUE)
channels = list(CHANNEL_COMMAND = TRUE, CHANNEL_SUPPLY = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/headset_service
name = "service radio encryption key"
@@ -162,7 +184,7 @@
/obj/item/device/encryptionkey/ert
name = "\improper ERT radio encryption key"
channels = list(CHANNEL_RESPONSE_TEAM = TRUE, CHANNEL_SCIENCE = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_MEDICAL = TRUE, CHANNEL_ENGINEERING = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_SUPPLY = TRUE, CHANNEL_SERVICE = TRUE)
channels = list(CHANNEL_RESPONSE_TEAM = TRUE, CHANNEL_SCIENCE = TRUE, CHANNEL_COMMAND = TRUE, CHANNEL_MEDICAL = TRUE, CHANNEL_ENGINEERING = TRUE, CHANNEL_SECURITY = TRUE, CHANNEL_SUPPLY = TRUE, CHANNEL_SERVICE = TRUE, CHANNEL_HAILING = TRUE)
/obj/item/device/encryptionkey/onlyert
name = "\improper ERT radio encryption key"

View File

@@ -29,13 +29,27 @@
keyslot1 = new ks1type(src)
if(ks2type)
keyslot2 = new ks2type(src)
set_listening(TRUE)
recalculateChannels(TRUE)
possibly_deactivate_in_loc()
moved_event.register(src, src, /obj/item/device/radio/headset/proc/possibly_deactivate_in_loc)
/obj/item/device/radio/headset/proc/possibly_deactivate_in_loc()
if(ismob(loc))
set_listening(should_be_listening)
else
set_listening(FALSE, actual_setting = FALSE)
/obj/item/device/radio/headset/Destroy()
QDEL_NULL(keyslot1)
QDEL_NULL(keyslot2)
return ..()
/obj/item/device/radio/headset/set_listening(new_listening, actual_setting = TRUE)
. = ..()
if(listening && on)
recalculateChannels()
/obj/item/device/radio/headset/list_channels(var/mob/user)
return list_secure_channels()
@@ -64,16 +78,16 @@
return ..()
/obj/item/device/radio/headset/receive_range(freq, level, aiOverride = 0)
/obj/item/device/radio/headset/can_receive(input_frequency, level, aiOverride = FALSE)
if (aiOverride)
return ..(freq, level)
return ..(input_frequency, level)
if(ishuman(src.loc))
var/mob/living/carbon/human/H = src.loc
if(H.l_ear == src || H.r_ear == src)
return ..(freq, level)
return ..(input_frequency, level)
if(!EarSound)
return ..(freq, level)
return -1
return ..(input_frequency, level)
return FALSE
/obj/item/device/radio/headset/attack_hand(mob/user)
if(ishuman(user))
@@ -127,10 +141,12 @@
/obj/item/device/radio/headset/proc/recalculateChannels(var/setDescription = FALSE)
src.channels = list()
src.translate_binary = FALSE
src.translate_hivenet = FALSE
src.syndie = FALSE
channels = list()
translate_binary = FALSE
translate_hivenet = FALSE
syndie = FALSE
SSradio.remove_object_all(src)
for(var/keyslot in list(keyslot1, keyslot2))
if(!keyslot)
@@ -138,31 +154,28 @@
var/obj/item/device/encryptionkey/K = keyslot
for(var/ch_name in K.channels)
if(ch_name in src.channels)
if(ch_name in channels)
continue
src.channels[ch_name] = K.channels[ch_name]
LAZYSET(channels, ch_name, K.channels[ch_name])
for(var/ch_name in K.additional_channels)
if(ch_name in src.channels)
if(ch_name in channels)
continue
src.channels[ch_name] = K.additional_channels[ch_name]
LAZYSET(channels, ch_name, K.additional_channels[ch_name])
if(K.translate_binary)
src.translate_binary = TRUE
translate_binary = TRUE
if(K.translate_hivenet)
src.translate_hivenet = TRUE
translate_hivenet = TRUE
if(K.syndie)
src.syndie = TRUE
syndie = TRUE
if(K.independent)
independent = TRUE
for (var/ch_name in channels)
if(!SSradio)
sleep(30) // Waiting for the SSradio to be created.
if(!SSradio)
src.name = "broken radio headset"
return
secure_radio_connections[ch_name] = SSradio.add_object(src, radiochannels[ch_name], RADIO_CHAT)
if(setDescription)
@@ -638,7 +651,7 @@
name = "military headset"
icon_state = "syn_headset"
origin_tech = list(TECH_ILLEGAL = 3)
syndie = 1
syndie = TRUE
ks1type = /obj/item/device/encryptionkey/syndicate
/obj/item/device/radio/headset/syndicate/alt
@@ -662,6 +675,7 @@
icon_state = "syn_headset"
origin_tech = list(TECH_ILLEGAL = 3)
syndie = TRUE
independent = TRUE
ks1type = /obj/item/device/encryptionkey/ninja
/obj/item/device/radio/headset/bluespace
@@ -670,12 +684,34 @@
icon_state = "bs_headset"
item_state = "com_headset" // laziness or genius, you decide
syndie = TRUE
independent = TRUE
ks1type = /obj/item/device/encryptionkey/bluespace
//Ghostrole headset
/obj/item/device/radio/headset/ship
icon_state = "syn_headset"
ks1type = /obj/item/device/encryptionkey/ship
var/use_common = FALSE
/obj/item/device/radio/headset/ship/Initialize()
if(!current_map.use_overmap)
return ..()
var/turf/T = get_turf(src)
var/obj/effect/overmap/visitable/V = map_sectors["[T.z]"]
if(istype(V) && V.comms_support)
default_frequency = assign_away_freq(V.name)
if(V.comms_name)
name = "[V.comms_name] radio headset"
. = ..()
if (use_common)
set_frequency(PUB_FREQ)
/obj/item/device/radio/headset/ship/common
use_common = TRUE
ks1type = /obj/item/device/encryptionkey/ship/common
/obj/item/device/radio/headset/binary
origin_tech = list(TECH_ILLEGAL = 3)
@@ -724,10 +760,8 @@
var/myAi = null // Atlantis: Reference back to the AI which has this radio.
var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu.
/obj/item/device/radio/headset/heads/ai_integrated/receive_range(freq, level)
if (disabledAi)
return -1 //Transciever Disabled.
return ..(freq, level, 1)
/obj/item/device/radio/headset/heads/ai_integrated/can_receive(input_frequency, level)
return ..(input_frequency, level, !disabledAi)
/obj/item/device/radio/headset/heads/ai_integrated/Destroy()
myAi = null

View File

@@ -16,45 +16,112 @@
var/radio_sound = null
clickvol = 40
/obj/item/device/radio/intercom/ship
channels = list()
var/default_hailing = FALSE
/obj/item/device/radio/intercom/ship/Initialize()
if(!current_map.use_overmap)
return ..()
var/turf/T = get_turf(src)
var/obj/effect/overmap/visitable/V = map_sectors["[T.z]"]
if(istype(V) && V.comms_support)
if(V.comms_name)
name = "intercom ([V.comms_name])"
default_frequency = assign_away_freq(V.name)
channels += list(
V.name = TRUE,
CHANNEL_HAILING = TRUE
)
. = ..()
if (default_hailing)
set_frequency(HAIL_FREQ)
/obj/item/device/radio/intercom/ship/hailing
default_hailing = TRUE
/obj/item/device/radio/intercom/custom
name = "intercom (custom)"
broadcasting = FALSE
listening = FALSE
/obj/item/device/radio/intercom/custom/Initialize()
. = ..()
set_broadcasting(FALSE)
set_listening(FALSE)
/obj/item/device/radio/intercom/hailing
name = "intercom (hailing)"
/obj/item/device/radio/intercom/hailing/Initialize()
. = ..()
set_frequency(HAIL_FREQ)
/obj/item/device/radio/intercom/interrogation
name = "intercom (interrogation)"
frequency = 1449
/obj/item/device/radio/intercom/interrogation/broadcasting // The detainee's side.
broadcasting = TRUE
listening = FALSE
/obj/item/device/radio/intercom/interrogation/Initialize()
. = ..()
set_frequency(1449)
/obj/item/device/radio/intercom/interrogation/broadcasting/Initialize() // The detainee's side.
set_broadcasting(TRUE)
set_listening(FALSE)
/obj/item/device/radio/intercom/private
name = "intercom (private)"
frequency = AI_FREQ
/obj/item/device/radio/intercom/private/Initialize()
. = ..()
set_frequency(AI_FREQ)
/obj/item/device/radio/intercom/specops
name = "intercom (spec ops)"
frequency = ERT_FREQ
/obj/item/device/radio/intercom/specops/Initialize()
. = ..()
set_frequency(ERT_FREQ)
/obj/item/device/radio/intercom/department
canhear_range = 5
broadcasting = FALSE
listening = TRUE
/obj/item/device/radio/intercom/department/Initialize()
. = ..()
set_broadcasting(FALSE)
set_listening(TRUE)
/obj/item/device/radio/intercom/department/medbay
name = "intercom (medical)"
frequency = MED_I_FREQ
/obj/item/device/radio/intercom/department/medbay/Initialize()
. = ..()
set_frequency(MED_I_FREQ)
internal_channels = default_medbay_channels.Copy()
/obj/item/device/radio/intercom/department/security
name = "intercom (security)"
frequency = SEC_I_FREQ
/obj/item/device/radio/intercom/department/security/Initialize()
. = ..()
set_frequency(SEC_I_FREQ)
internal_channels = list(
num2text(PUB_FREQ) = list(),
num2text(SEC_I_FREQ) = list(access_security)
)
/obj/item/device/radio/intercom/entertainment
name = "intercom (entertainment)"
frequency = ENT_FREQ
canhear_range = 4
/obj/item/device/radio/intercom/entertainment/Initialize()
. = ..()
set_frequency(ENT_FREQ)
internal_channels = list(
num2text(PUB_FREQ) = list(),
num2text(ENT_FREQ) = list()
)
/obj/item/device/radio/intercom/Initialize()
. = ..()
power_interface = new(loc, src)
@@ -70,44 +137,26 @@
screen_overlays["intercom_b"] = make_screen_overlay(icon, "intercom_b")
screen_overlays["intercom_l"] = make_screen_overlay(icon, "intercom_l")
/obj/item/device/radio/intercom/department/medbay/Initialize()
. = ..()
internal_channels = default_medbay_channels.Copy()
/obj/item/device/radio/intercom/department/security/Initialize()
. = ..()
internal_channels = list(
num2text(PUB_FREQ) = list(),
num2text(SEC_I_FREQ) = list(access_security)
)
/obj/item/device/radio/intercom/entertainment/Initialize()
. = ..()
internal_channels = list(
num2text(PUB_FREQ) = list(),
num2text(ENT_FREQ) = list()
)
/obj/item/device/radio/intercom/syndicate
name = "illegally modified intercom"
desc = "Talk through this. Evilly."
frequency = SYND_FREQ
subspace_transmission = 1
syndie = 1
subspace_transmission = TRUE
syndie = TRUE
/obj/item/device/radio/intercom/syndicate/Initialize()
. = ..()
set_frequency(SYND_FREQ)
internal_channels[num2text(SYND_FREQ)] = list(access_syndicate)
/obj/item/device/radio/intercom/raider
name = "illegally modified intercom"
desc = "Pirate radio, but not in the usual sense of the word."
frequency = RAID_FREQ
subspace_transmission = 1
syndie = 1
subspace_transmission = TRUE
syndie = TRUE
/obj/item/device/radio/intercom/syndicate/Initialize()
. = ..()
set_frequency(RAID_FREQ)
internal_channels[num2text(RAID_FREQ)] = list(access_syndicate)
/obj/item/device/radio/intercom/Destroy()
@@ -124,27 +173,19 @@
src.add_fingerprint(user)
INVOKE_ASYNC(src, /obj/item/.proc/attack_self, user)
/obj/item/device/radio/intercom/receive_range(freq, level)
if (!on)
return -1
if(!(0 in level))
/obj/item/device/radio/intercom/can_receive(input_frequency, list/levels)
if(levels != RADIO_NO_Z_LEVEL_RESTRICTION)
var/turf/position = get_turf(src)
if(isnull(position) || !(position.z in level))
return -1
if (!src.listening)
return -1
if(freq in ANTAG_FREQS)
if(!(src.syndie))
return -1//Prevents broadcast of messages over devices lacking the encryption
if(!istype(position) || !(position.z in levels))
return FALSE
return canhear_range
if(input_frequency in ANTAG_FREQS && !syndie)
return FALSE//Prevents broadcast of messages over devices lacking the encryption
return TRUE
/obj/item/device/radio/intercom/proc/power_change(has_power)
if (!src.loc)
on = 0
else
on = has_power
set_on(has_power) // has_power is given by our listener machinery
update_icon()
/obj/item/device/radio/intercom/forceMove(atom/dest)
@@ -166,8 +207,8 @@
if(listening)
add_overlay(screen_overlays["intercom_l"])
/obj/item/device/radio/intercom/broadcasting
broadcasting = TRUE
/obj/item/device/radio/intercom/broadcasting/Initialize()
set_broadcasting(TRUE)
/obj/item/device/radio/intercom/locked
var/locked_frequency
@@ -181,10 +222,16 @@
/obj/item/device/radio/intercom/locked/ai_private
name = "intercom (AI private)"
frequency = AI_FREQ
broadcasting = TRUE
listening = TRUE
/obj/item/device/radio/intercom/locked/ai_private/Initialize()
. = ..()
set_frequency(AI_FREQ)
set_broadcasting(TRUE)
set_listening(TRUE)
/obj/item/device/radio/intercom/locked/confessional
name = "intercom (confessional)"
frequency = 1480
/obj/item/device/radio/intercom/locked/confessional/Initialize()
. = ..()
set_frequency(1480)

View File

@@ -34,19 +34,6 @@ var/global/list/default_medbay_channels = list(
suffix = "\[3\]"
icon_state = "walkietalkie"
item_state = "radio"
var/on = 1 // 0 for off
var/last_transmission
var/frequency = PUB_FREQ //common chat
var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies
var/canhear_range = 3 // the range which mobs can hear this radio from
var/mob/living/announcer/announcer = null // used in autosay, held by the radio for re-use
var/datum/wires/radio/wires = null
var/b_stat = 0
var/broadcasting = FALSE
var/listening = TRUE
var/list/channels = list() //see communications.dm for full list. First non-common, non-entertainment channel is a "default" for :h
var/subspace_transmission = 0
var/syndie = 0//Holder to see if it's a syndicate encrypted radio
flags = CONDUCT
slot_flags = SLOT_BELT
throw_speed = 2
@@ -61,39 +48,131 @@ var/global/list/default_medbay_channels = list(
var/obj/item/cell/cell = /obj/item/cell/device
var/last_radio_sound = -INFINITY
/obj/item/device/radio
var/datum/radio_frequency/radio_connection
var/list/datum/radio_frequency/secure_radio_connections = new
// If FALSE, broadcasting and listening don't matter and this radio does nothing
VAR_PRIVATE/on = TRUE
proc/set_frequency(new_frequency)
VAR_PRIVATE/frequency = PUB_FREQ // Current frequency the radio is set to
var/default_frequency = PUB_FREQ // frequency the radio defaults to on reset / startup
// Whether the radio transmits dialogue it hears nearby onto its radio channel
VAR_PRIVATE/broadcasting = FALSE
// Whether the radio is currently receiving radio messages from its frequencies
VAR_PRIVATE/listening = TRUE
//the below vars are used to track listening and broadcasting should they be forced off for whatever reason but "supposed" to be active
//eg player sets the radio to listening, but an emp or whatever turns it off, its still supposed to be activated but was forced off,
//when it wears off it sets listening to should_be_listening
///used for tracking what broadcasting should be in the absence of things forcing it off, eg its set to broadcast but gets emp'd temporarily
var/should_be_broadcasting = FALSE
///used for tracking what listening should be in the absence of things forcing it off, eg its set to listen but gets emp'd temporarily
var/should_be_listening = TRUE
/// Both the range around the radio in which mobs can hear what it receives and the range the radio can hear
var/canhear_range = 3
var/last_transmission
var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies
var/mob/living/announcer/announcer = null // used in autosay, held by the radio for re-use
var/datum/wires/radio/wires = null
var/b_stat = 0
var/list/channels = list() //see communications.dm for full list. First non-common, non-entertainment channel is a "default" for :h
var/subspace_transmission = FALSE
var/syndie = FALSE //Holder to see if it's a syndicate encrypted radio
var/independent = FALSE // if TRUE, can say/hear on the Special Channel!!! (TBD)
var/datum/radio_frequency/radio_connection
var/list/datum/radio_frequency/secure_radio_connections = list()
/obj/item/device/radio/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT)
/obj/item/device/radio/Destroy()
listening_objects -= src
QDEL_NULL(announcer)
QDEL_NULL(wires)
if(SSradio)
SSradio.remove_object(src, frequency)
for (var/ch_name in channels)
SSradio.remove_object(src, radiochannels[ch_name])
return ..()
/obj/item/device/radio/Initialize()
. = ..()
wires = new(src)
internal_channels = default_internal_channels.Copy()
listening_objects += src
become_hearing_sensitive(ROUNDSTART_TRAIT)
if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ)
frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ)
set_frequency(frequency)
for (var/ch_name in channels)
secure_radio_connections[ch_name] = SSradio.add_object(src, radiochannels[ch_name], RADIO_CHAT)
set_listening(listening)
set_broadcasting(broadcasting)
set_frequency(default_frequency)
set_on(on)
/obj/item/device/radio/Destroy()
SSradio.remove_object_all(src)
QDEL_NULL(announcer)
QDEL_NULL(wires)
return ..()
/obj/item/device/radio/proc/is_on()
return on
/obj/item/device/radio/proc/get_frequency()
return frequency
/obj/item/device/radio/proc/get_broadcasting()
return broadcasting
/obj/item/device/radio/proc/get_listening()
return listening
/**
* setter for the listener var, adds or removes this radio from the global radio list if we are also on
*
* * new_listening - the new value we want to set listening to
* * actual_setting - whether or not the radio is supposed to be listening, sets should_be_listening to the new listening value if true, otherwise just changes listening
*/
/obj/item/device/radio/proc/set_listening(new_listening, actual_setting = TRUE)
listening = new_listening
if(actual_setting)
should_be_listening = listening
if(listening && on)
SSradio.add_object(src, frequency, RADIO_CHAT)
else if(!listening)
SSradio.remove_object_all(src)
/**
* setter for broadcasting that makes us not hearing sensitive if not broadcasting and hearing sensitive if broadcasting
* hearing sensitive in this case only matters for the purposes of listening for words said in nearby tiles, talking into us directly bypasses hearing
*
* * new_broadcasting- the new value we want to set broadcasting to
* * actual_setting - whether or not the radio is supposed to be broadcasting, sets should_be_broadcasting to the new value if true, otherwise just changes broadcasting
*/
/obj/item/device/radio/proc/set_broadcasting(new_broadcasting, actual_setting = TRUE)
broadcasting = new_broadcasting
if(actual_setting)
should_be_broadcasting = broadcasting
if(broadcasting && on) //we dont need hearing sensitivity if we arent broadcasting, because talk_into doesnt care about hearing
become_hearing_sensitive(INNATE_TRAIT)
else if(!broadcasting)
lose_hearing_sensitivity(INNATE_TRAIT)
///setter for the on var that sets both broadcasting and listening to off or whatever they were supposed to be
/obj/item/device/radio/proc/set_on(new_on)
on = new_on
if(on)
set_broadcasting(should_be_broadcasting)//set them to whatever theyre supposed to be
set_listening(should_be_listening)
else
set_broadcasting(FALSE, actual_setting = FALSE)//fake set them to off
set_listening(FALSE, actual_setting = FALSE)
/obj/item/device/radio/attack_self(mob/user as mob)
user.set_machine(src)
interact(user)
@@ -113,6 +192,7 @@ var/global/list/default_medbay_channels = list(
data["mic_status"] = broadcasting
data["speaker"] = listening
data["freq"] = format_frequency(frequency)
data["default_freq"] = format_frequency(default_frequency)
data["rawfreq"] = num2text(frequency)
data["mic_cut"] = (wires.IsIndexCut(WIRE_TRANSMIT) || wires.IsIndexCut(WIRE_SIGNAL))
@@ -134,9 +214,15 @@ var/global/list/default_medbay_channels = list(
/obj/item/device/radio/proc/setupRadioDescription(var/additional_radio_desc)
var/radio_text = ""
var/found_first_department = FALSE
for(var/i = 1 to channels.len)
var/channel = channels[i]
var/key = get_radio_key_from_channel(channel)
if(!length(key) && !found_first_department && channel != CHANNEL_COMMON && channel != CHANNEL_ENTERTAINMENT)
// if we don't have a key and it's the 'shortcut' channel, put the department key instead
key = get_radio_key_from_channel("department")
found_first_department = TRUE
radio_text += "[key] - [channel]"
if(i != channels.len)
radio_text += ", "
@@ -196,12 +282,6 @@ var/global/list/default_medbay_channels = list(
Speaker: <A href='byond://?src=\ref[src];ch_name=[chan_name];listen=[!list]'>[list ? "Engaged" : "Disengaged"]</A><BR>
"}
/obj/item/device/radio/proc/ToggleBroadcast()
broadcasting = !broadcasting && !(wires.IsIndexCut(WIRE_TRANSMIT) || wires.IsIndexCut(WIRE_SIGNAL))
/obj/item/device/radio/proc/ToggleReception()
listening = !listening && !(wires.IsIndexCut(WIRE_RECEIVE) || wires.IsIndexCut(WIRE_SIGNAL))
/obj/item/device/radio/CanUseTopic()
if(!on)
return STATUS_CLOSE
@@ -214,7 +294,7 @@ var/global/list/default_medbay_channels = list(
/obj/item/device/radio/Topic(href, href_list)
if(..())
return 1
return TRUE
usr.set_machine(src)
if (href_list["track"])
@@ -222,7 +302,7 @@ var/global/list/default_medbay_channels = list(
var/mob/living/silicon/ai/A = locate(href_list["track2"])
if(A && target)
A.ai_actual_track(target)
. = 1
. = TRUE
else if (href_list["freq"])
var/new_frequency = (frequency + text2num(href_list["freq"]))
@@ -232,27 +312,32 @@ var/global/list/default_medbay_channels = list(
if(hidden_uplink)
if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency))
usr << browse(null, "window=radio")
. = 1
. = TRUE
else if (href_list["talk"])
ToggleBroadcast()
. = 1
set_broadcasting(!broadcasting)
. = TRUE
else if (href_list["listen"])
var/chan_name = href_list["ch_name"]
if (!chan_name)
ToggleReception()
set_listening(!listening)
else
if (channels[chan_name] & FREQ_LISTENING)
channels[chan_name] &= ~FREQ_LISTENING
else
channels[chan_name] |= FREQ_LISTENING
. = 1
. = TRUE
else if(href_list["spec_freq"])
var freq = href_list["spec_freq"]
if(has_channel_access(usr, freq))
set_frequency(text2num(freq))
. = 1
. = TRUE
else if(href_list["reset_freq"])
if(default_frequency)
set_frequency(default_frequency)
. = TRUE
if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk
return 1
return TRUE
if(.)
SSnanoui.update_uis(src)
@@ -277,16 +362,13 @@ var/global/list/default_medbay_channels = list(
if (!istype(connection))
return
if (!connection)
return
if(!istype(announcer))
announcer = new()
announcer.PrepareBroadcast(from)
Broadcast_Message(connection, announcer,
FALSE, "*garbled automated announcement*", src,
message, from, "Automated Announcement", from, announcer.voice_name,
4, 0, list(0), connection.frequency, "states", announcer.default_language)
var/datum/weakref/speaker_weakref = WEAKREF(announcer)
var/datum/signal/subspace/vocal/signal = new(src, frequency, speaker_weakref, announcer.default_language, message, "states")
signal.send_to_receivers()
announcer.ResetAfterBroadcast()
// Interprets the message mode when talking into a radio, possibly returning a connection datum
@@ -311,12 +393,13 @@ var/global/list/default_medbay_channels = list(
// If we were to send to a channel we don't have, drop it.
return null
/obj/item/device/radio/talk_into(mob/living/M, message, channel, var/verb = "says", var/datum/language/speaking = null, var/ignore_restrained)
/obj/item/device/radio/talk_into(mob/living/M, message, channel, var/say_verb = "says", var/datum/language/speaking = null, var/ignore_restrained)
if(!on)
return 0 // the device has to be on
// Fix for permacell radios, but kinda eh about actually fixing them.
return FALSE
if(!M || !message)
return 0
return FALSE
if(wires.IsIndexCut(WIRE_TRANSMIT)) // The device has to have all its wires and shit intact
return FALSE
if (iscarbon(M))
var/mob/living/carbon/C = M
@@ -333,268 +416,85 @@ var/global/list/default_medbay_channels = list(
return FALSE
M.trigger_aiming(TARGET_CAN_RADIO)
// Uncommenting this. To the above comment:
// The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom
if(wires.IsIndexCut(WIRE_TRANSMIT)) // The device has to have all its wires and shit intact
return 0
if(!radio_connection)
set_frequency(frequency)
if(loc == M)
playsound(loc, 'sound/effects/walkietalkie.ogg', 5, 0, -1, required_asfx_toggles = ASFX_RADIO)
/* Quick introduction:
This new radio system uses a very robust FTL signaling technology unoriginally
dubbed "subspace" which is somewhat similar to 'blue-space' but can't
actually transmit large mass. Headsets are the only radio devices capable
of sending subspace transmissions to the Communications Satellite.
A headset sends a signal to a subspace listener/receiver elsewhere in space,
the signal gets processed and logged, and an audible transmission gets sent
to each individual headset.
/*
Roughly speaking, radios attempt to make a subspace transmission (which
is received, processed, and rebroadcast by the telecomms satellite) and
if that fails, they send a mundane radio transmission.
Headsets cannot send/receive mundane transmissions, only subspace.
Syndicate radios can hear transmissions on all well-known frequencies.
CentCom radios can hear the CentCom frequency no matter what.
*/
//#### Grab the connection datum ####//
// Get the frequency
var/datum/radio_frequency/connection = handle_message_mode(M, message, channel)
if (!istype(connection))
return 0
if (!connection)
return 0
return FALSE
var/turf/position = get_turf(src)
// Determine the identify information attached to the signal
var/datum/weakref/speaker_weakref = WEAKREF(M)
var/datum/signal/subspace/vocal/signal = new(src, connection.frequency, speaker_weakref, speaking, message, say_verb)
var/obj/effect/overmap/visitable/sector
if(current_map.use_overmap)
var/my_sector = map_sectors["[position.z]"]
if(istype(my_sector, /obj/effect/overmap/visitable))
sector = my_sector
//#### Tagging the signal with all appropriate identity values ####//
// ||-- The mob's name identity --||
var/displayname = M.name // grab the display name (name you get when you hover over someone's icon)
var/real_name = M.real_name // mob's real name
var/mobkey = "none" // player key associated with mob
var/voicemask = 0 // the speaker is wearing a voice mask
if(M.client)
mobkey = M.key // assign the mob's key
var/jobname // the mob's "job"
// --- Human: use their actual job ---
if (ishuman(M))
var/mob/living/carbon/human/H = M
jobname = H.get_assignment()
// --- Carbon Nonhuman ---
else if (iscarbon(M)) // Nonhuman carbon mob
jobname = "No id"
// --- AI ---
else if (isAI(M))
jobname = "AI"
// --- Cyborg ---
else if (isrobot(M))
jobname = "Cyborg"
// --- Personal AI (pAI) ---
else if (istype(M, /mob/living/silicon/pai))
jobname = "Personal AI"
// --- Unidentifiable mob ---
else
jobname = "Unknown"
// --- Modifications to the mob's identity ---
// The mob is disguising their identity:
if (ishuman(M) && M.GetVoice() != real_name)
displayname = M.GetVoice()
jobname = "Unknown"
voicemask = 1
/* ###### Radio headsets can only broadcast through subspace ###### */
// All radios attempt to use the subspace system
signal.send_to_receivers()
// If it's subspace only, that's all we can do
if(subspace_transmission)
// Check for jamming.
if (within_jamming_range(src))
return
// First, we want to generate a new radio signal
var/datum/signal/signal = new
signal.transmission_method = TRANSMISSION_SUBSPACE
// --- Finally, tag the actual signal with the appropriate values ---
signal.data = list(
// Identity-associated tags:
"mob" = M, // store a reference to the mob
"mobtype" = M.type, // the mob's type
"realname" = real_name, // the mob's real name
"name" = displayname, // the mob's display name
"job" = jobname, // the mob's job
"key" = mobkey, // the mob's key
"vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood
"vname" = M.voice_name, // the name to display if the voice wasn't understood
"vmask" = voicemask, // 1 if the mob is using a voice gas mask
// Non-subspace radios will check in a couple of seconds, and if the signal was never received, we send a mundane broadcast
addtimer(CALLBACK(src, .proc/backup_transmission, signal), 2 SECONDS)
// We store things that would otherwise be kept in the actual mob
// so that they can be logged even AFTER the mob is deleted or something
// Other tags:
"compression" = rand(45,50), // compressed radio signal
"message" = message, // the actual sent message
"connection" = connection, // the radio connection to use
"radio" = src, // stores the radio used for transmission
"slow" = 0, // how much to sleep() before broadcasting - simulates net lag
"traffic" = 0, // dictates the total traffic sum that the signal went through
"type" = 0, // determines what type of radio input it is: normal broadcast
"server" = null, // the last server to log this signal
"reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery
"level" = position.z, // The source's z level
"language" = speaking,
"verb" = verb,
"sector" = sector
)
signal.frequency = connection.frequency // Quick frequency set
//#### Sending the signal to all subspace receivers ####//
for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
INVOKE_ASYNC(R, /obj/proc/receive_signal, signal)
// Allinone can act as receivers.
for(var/obj/machinery/telecomms/allinone/R in telecomms_list)
INVOKE_ASYNC(R, /obj/proc/receive_signal, signal)
// Receiving code can be located in Telecommunications.dm
var/position_z_in_level = FALSE // this particular band-aid is required to make antag radios say "talks into" and not "tries to talk into" when using a radio, due to how their signals are made
if(islist(signal.data["level"]))
if(position.z in signal.data["level"])
position_z_in_level = TRUE
else
if(position.z == signal.data["level"])
position_z_in_level = TRUE
return signal.data["done"] && position_z_in_level
/* ###### Intercoms and station-bounced radios ###### */
var/filter_type = 2
/* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */
if(istype(src, /obj/item/device/radio/intercom))
filter_type = 1
var/datum/signal/signal = new
signal.transmission_method = TRANSMISSION_SUBSPACE
/* --- Try to send a normal subspace broadcast first */
signal.data = list(
"mob" = M, // store a reference to the mob
"mobtype" = M.type, // the mob's type
"realname" = real_name, // the mob's real name
"name" = displayname, // the mob's display name
"job" = jobname, // the mob's job
"key" = mobkey, // the mob's key
"vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood
"vname" = M.voice_name, // the name to display if the voice wasn't understood
"vmask" = voicemask, // 1 if the mob is using a voice gas mas
"compression" = 0, // uncompressed radio signal
"message" = message, // the actual sent message
"connection" = connection, // the radio connection to use
"radio" = src, // stores the radio used for transmission
"slow" = 0,
"traffic" = 0,
"type" = 0,
"server" = null,
"reject" = 0,
"level" = position.z,
"language" = speaking,
"verb" = verb,
"sector" = sector
)
signal.frequency = connection.frequency // Quick frequency set
for(var/obj/machinery/telecomms/receiver/R in telecomms_list)
INVOKE_ASYNC(R, /obj/proc/receive_signal, signal)
sleep(rand(10,25)) // wait a little...
if(signal.data["done"] && (position.z in signal.data["level"]))
// we're done here.
return 1
// Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level.
// Send a mundane broadcast with limited targets:
//THIS IS TEMPORARY. YEAH RIGHT
if(!connection) return 0 //~Carn
return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote),
src, message, displayname, jobname, real_name, M.voice_name,
filter_type, signal.data["compression"], list(position.z), connection.frequency,verb,speaking)
/obj/item/device/radio/proc/backup_transmission(datum/signal/subspace/vocal/signal)
var/turf/T = get_turf(src)
if (signal.data["done"] && (T.z in signal.levels))
return
// If we're here, the signal was never processed. Proceed with mundane broadcast:
signal.data["compression"] = 0
signal.transmission_method = TRANSMISSION_RADIO
signal.levels = list(T.z)
signal.broadcast()
/obj/item/device/radio/hear_talk(mob/M as mob, msg, var/verb = "says", var/datum/language/speaking = null)
if (!broadcasting || get_dist(src, M) > canhear_range)
return
if (broadcasting)
if(get_dist(src, M) <= canhear_range)
talk_into(M, msg, null, verb, speaking, ignore_restrained = TRUE)
/obj/item/device/radio/proc/receive_range(freq, level)
// check if this radio can receive on the given frequency, and if so,
// what the range is in which mobs will hear the radio
// returns: -1 if can't receive, range otherwise
if (wires.IsIndexCut(WIRE_RECEIVE))
return -1
if(!listening)
return -1
if (within_jamming_range(src))
return -1
if(!(0 in level))
/obj/item/device/radio/proc/can_receive(input_frequency, list/levels)
// check if the radio can receive on the given frequency
if (levels != RADIO_NO_Z_LEVEL_RESTRICTION)
var/turf/position = get_turf(src)
if(!position || !(position.z in level))
return -1
if(freq in ANTAG_FREQS)
if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys
return -1
if (!on)
return -1
if (!freq) //received on main frequency
if (!listening)
return -1
else
var/accept = (freq==frequency && listening)
if (!accept)
if (!position || !(position.z in levels))
return FALSE
if (within_jamming_range(src))
return FALSE
if ((input_frequency in ANTAG_FREQS) && !syndie) //Checks to see if it's allowed on that frequency, based on the encryption keys
return FALSE
if (input_frequency == frequency)
return TRUE
for (var/ch_name in channels)
var/datum/radio_frequency/RF = secure_radio_connections[ch_name]
if (RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING))
accept = 1
break
if (!accept)
return -1
return canhear_range
if (RF.frequency == input_frequency && (channels[ch_name] & FREQ_LISTENING))
return TRUE
return FALSE
/obj/item/device/radio/proc/send_hear(freq, level)
if(!can_receive(freq, level))
return
var/range = receive_range(freq, level)
if(range > -1)
var/list/mobs = list()
var/list/objs = list()
get_mobs_or_objs_in_view(get_turf(src), canhear_range, mobs, objs)
return mobs
return get_hearers_in_view(canhear_range, src)
/obj/item/device/radio/examine(mob/user)
@@ -624,8 +524,8 @@ var/global/list/default_medbay_channels = list(
else return
/obj/item/device/radio/emp_act(severity)
broadcasting = FALSE
listening = FALSE
set_broadcasting(FALSE)
set_listening(FALSE)
for (var/ch_name in channels)
channels[ch_name] = 0
..()
@@ -719,6 +619,9 @@ var/global/list/default_medbay_channels = list(
if(keyslot.syndie)
syndie = TRUE
if(keyslot.independent)
independent = TRUE
for(var/ch_name in src.channels)
if(!SSradio)
sleep(30) // Waiting for the SSradio to be created.
@@ -774,6 +677,7 @@ var/global/list/default_medbay_channels = list(
data["mic_status"] = broadcasting
data["speaker"] = listening
data["freq"] = format_frequency(frequency)
data["default_freq"] = format_frequency(default_frequency)
data["rawfreq"] = num2text(frequency)
var/list/chanlist = list_channels(user)
@@ -799,7 +703,7 @@ var/global/list/default_medbay_channels = list(
if(SSradio)
for (var/ch_name in channels)
SSradio.remove_object(src, radiochannels[ch_name])
secure_radio_connections = new
secure_radio_connections = list()
channels = op
if(SSradio)
for (var/ch_name in op)
@@ -810,57 +714,83 @@ var/global/list/default_medbay_channels = list(
// Radio Variants
//
/obj/item/device/radio/map_preset
channels = list()
/obj/item/device/radio/map_preset/Initialize()
if(!current_map.use_overmap)
return ..()
var/turf/T = get_turf(src)
var/obj/effect/overmap/visitable/V = map_sectors["[T.z]"]
if(istype(V) && V.comms_support)
frequency = assign_away_freq(V.name)
channels += list(
V.name = TRUE,
CHANNEL_HAILING = TRUE
)
if(V.comms_name)
name = "[V.comms_name] shortwave radio"
return ..()
// Radio (Off)
/obj/item/device/radio/off
listening = FALSE
/obj/item/device/radio/off/Initialize()
. = ..()
set_listening(FALSE)
// Medical
/obj/item/device/radio/med
icon_state = "walkietalkie-med"
// Medical (Off)
/obj/item/device/radio/med/off
listening = FALSE
/obj/item/device/radio/med/off/Initialize()
. = ..()
set_listening(FALSE)
// Security
/obj/item/device/radio/sec
icon_state = "walkietalkie-sec"
// Security (Off)
/obj/item/device/radio/sec/off
listening = FALSE
/obj/item/device/radio/sec/off/Initialize()
. = ..()
set_listening(FALSE)
// Engineering
/obj/item/device/radio/eng
icon_state = "walkietalkie-eng"
// Engineering (Off)
/obj/item/device/radio/eng/off
listening = FALSE
/obj/item/device/radio/eng/off/Initialize()
. = ..()
set_listening(FALSE)
// Science
/obj/item/device/radio/sci
icon_state = "walkietalkie-sci"
// Science (Off)
/obj/item/device/radio/sci/off
listening = FALSE
/obj/item/device/radio/sci/off/Initialize()
. = ..()
set_listening(FALSE)
// Phone
/obj/item/device/radio/phone
broadcasting = FALSE
icon = 'icons/obj/radio.dmi'
icon_state = "red_phone"
listening = TRUE
name = "phone"
var/radio_sound = null
// Medical Phone
/obj/item/device/radio/phone/medbay
frequency = MED_I_FREQ
/obj/item/device/radio/phone/Initialize()
. = ..()
set_broadcasting(FALSE)
set_listening(TRUE)
// Medical Phone
/obj/item/device/radio/phone/medbay/Initialize()
. = ..()
set_frequency(MED_I_FREQ)
internal_channels = default_medbay_channels.Copy()
// All-channel Radio

View File

@@ -23,11 +23,7 @@
..()
radio = new(src)
camera = new(src)
listening_objects += src
/obj/item/device/spy_bug/Destroy()
listening_objects -= src
return ..()
become_hearing_sensitive(ROUNDSTART_TRAIT)
/obj/item/device/spy_bug/examine(mob/user)
. = ..(user, 0)
@@ -36,8 +32,8 @@
to_chat(user, "Needs to be both configured and brought in contact with monitor device to be fully functional.")
/obj/item/device/spy_bug/attack_self(mob/user)
radio.broadcasting = !radio.broadcasting
to_chat(user, "\The [src]'s radio is [radio.broadcasting ? "broadcasting" : "not broadcasting"] now. The current frequency is [radio.frequency].")
radio.set_broadcasting(!radio.get_broadcasting())
to_chat(user, "\The [src]'s radio is [radio.get_broadcasting() ? "broadcasting" : "not broadcasting"] now. The current frequency is [radio.get_frequency()].")
radio.attack_self(user)
/obj/item/device/spy_bug/attackby(obj/W as obj, mob/living/user as mob)
@@ -70,11 +66,7 @@
/obj/item/device/spy_monitor/New()
radio = new(src)
listening_objects += src
/obj/item/device/spy_monitor/Destroy()
listening_objects -= src
return ..()
become_hearing_sensitive(ROUNDSTART_TRAIT)
/obj/item/device/spy_monitor/examine(mob/user)
. = ..(user, 1)
@@ -161,9 +153,12 @@
return 0
/obj/item/device/radio/spy
listening = FALSE
frequency = 1473
broadcasting = FALSE
canhear_range = 7
name = "spy device"
icon_state = "syn_cypherkey"
/obj/item/device/radio/spy/Initialize()
. = ..()
set_broadcasting(FALSE)
set_listening(FALSE)
set_frequency(1473)

View File

@@ -23,7 +23,7 @@
/obj/item/device/taperecorder/Initialize()
. = ..()
listening_objects += src
become_hearing_sensitive(ROUNDSTART_TRAIT)
portable_drive = new /obj/item/computer_hardware/hard_drive/portable(src)
/obj/item/device/taperecorder/Destroy()

View File

@@ -24,15 +24,6 @@
"/obj/item/stack/cable_coil" = 2,
"/obj/item/stock_parts/subspace/filter" = 2)
/obj/item/circuitboard/telecomms/relay
name = T_BOARD("relay mainframe")
build_path = /obj/machinery/telecomms/relay
origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 4, TECH_BLUESPACE = 3)
req_components = list(
"/obj/item/stock_parts/manipulator" = 2,
"/obj/item/stack/cable_coil" = 2,
"/obj/item/stock_parts/subspace/filter" = 2)
/obj/item/circuitboard/telecomms/bus
name = T_BOARD("bus mainframe")
build_path = /obj/machinery/telecomms/bus

View File

@@ -7,15 +7,15 @@
/obj/item/grenade/flashbang/prime()
..()
for(var/obj/structure/closet/L in hear(7, get_turf(src)))
for(var/obj/structure/closet/L in get_hear(7, get_turf(src)))
if(locate(/mob/living/carbon/, L))
for(var/mob/living/carbon/M in L)
bang(get_turf(src), M)
for(var/mob/living/carbon/M in hear(7, get_turf(src)))
for(var/mob/living/carbon/M in get_hear(7, get_turf(src)))
bang(get_turf(src), M)
for(var/obj/effect/blob/B in hear(8,get_turf(src))) //Blob damage here
for(var/obj/effect/blob/B in get_hear(8,get_turf(src))) //Blob damage here
var/damage = round(30/(get_dist(B,get_turf(src))+1))
B.health -= damage
B.update_icon()

View File

@@ -289,10 +289,9 @@ Implant Specifics:<BR>"}
/obj/item/implant/explosive/New()
..()
listening_objects += src
become_hearing_sensitive(ROUNDSTART_TRAIT)
/obj/item/implant/explosive/Destroy()
listening_objects -= src
return ..()
/obj/item/implant/explosive/full

View File

@@ -77,7 +77,7 @@ Frequency:
src.temp += "<B>Located Beacons:</B><BR>"
for(var/obj/item/device/radio/beacon/W in teleportbeacons)
if (W.frequency == src.frequency)
if (W.get_frequency() == src.frequency)
var/turf/tr = get_turf(W)
if (tr.z == sr.z && tr)
var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))

View File

@@ -184,7 +184,7 @@
being_shocked = 0
/obj/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
/obj/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
return
//To be called from things that spill objects on the floor.

View File

@@ -32,7 +32,7 @@
icon_state = "morgue0"
else
icon_state = "morgue1"
var/list/searching = GetAllContents(searchDepth = 3, checkClient = 0) // Search inside bodybags as well.
var/list/searching = get_all_contents_of_type(/mob/living) // Search inside bodybags as well.
for(var/mob/living/M in searching)
if(iscarbon(M))
var/mob/living/carbon/C = M
@@ -252,7 +252,7 @@
update_icon()
var/desperation = 0
var/list/searching = GetAllContents(searchDepth = 3, checkClient = 0)
var/list/searching = get_all_contents_of_type(/mob/living)
for(var/mob/living/M in searching)
admin_attack_log(A, M, "Began cremating their victim.", "Has begun being cremated.", "began cremating")
if(iscarbon(M))

View File

@@ -120,13 +120,10 @@
M.playsound_to(source_turf, S, use_random_freq = use_random_freq, use_pressure = use_pressure, modify_environment = modify_environment)
/proc/playsound_lineofsight(atom/source, sound/S, use_random_freq = FALSE, use_pressure = TRUE, modify_environment = TRUE, required_preferences = 0, required_asfx_toggles = 0)
var/list/hearers = get_hearers_in_view(world.view, source)
var/turf/source_turf = get_turf(source)
var/list/mobs = list()
var/list/objs = list()
get_mobs_or_objs_in_view(source_turf, world.view, mobs, objs, ONLY_GHOSTS_IN_VIEW)
for (var/MM in mobs)
var/mob/M = MM
for (var/mob/M in hearers)
if (!M.sound_can_play(required_preferences, required_asfx_toggles))
continue

View File

@@ -39,6 +39,9 @@
turfs += src
if (isStationLevel(z))
station_turfs += src
if(dynamic_lighting)
luminosity = 0
else

View File

@@ -66,6 +66,9 @@
turfs += src
if (isStationLevel(z))
station_turfs += src
if(dynamic_lighting)
luminosity = 0
else
@@ -109,6 +112,9 @@
changing_turf = FALSE
turfs -= src
if (isStationLevel(z))
station_turfs -= src
remove_cleanables()
cleanup_roof()

View File

@@ -175,14 +175,13 @@
/obj/item/device/assembly_holder/Initialize(mapload, ...)
. = ..()
listening_objects += src
become_hearing_sensitive()
/obj/item/device/assembly_holder/Destroy()
if(a_left)
a_left.holder = null
if(a_right)
a_right.holder = null
listening_objects -= src
return ..()
/obj/item/device/assembly_holder/hear_talk(mob/living/M, msg, verb, datum/language/speaking)

View File

@@ -11,10 +11,9 @@
/obj/item/device/assembly/voice/Initialize(mapload, ...)
. = ..()
listening_objects += src
become_hearing_sensitive()
/obj/item/device/assembly/voice/Destroy()
listening_objects -= src
return ..()
/obj/item/device/assembly/voice/hear_talk(mob/living/M, msg)

View File

@@ -15,9 +15,9 @@
/atom/proc/balloon_alert_to_viewers(message, self_message, vision_distance = 7, list/ignored_mobs)
SHOULD_NOT_SLEEP(TRUE)
var/list/hearers = list()
var/list/objs = list()
get_mobs_or_objs_in_view(get_turf(src), vision_distance, hearers, objs, ONLY_GHOSTS_IN_VIEW)
// This has to be unlinted because the linter thinks that oranges ears will add reagents and call send_asset
// which violates SHOULD_NOT_SLEEP(TRUE)
var/list/hearers = UNLINT(get_hearers_in_view(vision_distance, src))
hearers -= ignored_mobs
for(var/mob/hearer as anything in hearers - src)

View File

@@ -88,9 +88,7 @@
// It handles items shipped for bounties.
/datum/controller/subsystem/cargo/proc/bounty_ship_item_and_contents(atom/movable/AM, dry_run=FALSE)
var/list/matched_one = FALSE
var/list/contents = list()
contents += AM
contents += AM.GetAllContents()
var/list/contents = AM.GetAllContents()
for(var/thing in reverseRange(contents))
var/matched_this = FALSE
for(var/datum/bounty/B in bounties_list)
@@ -227,4 +225,3 @@
if(B.claimed)
++count
return count

View File

@@ -30,9 +30,7 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they
var/sold_str = ""
var/cost = 0
var/list/contents = list()
contents += AM //Also add the container object
contents += AM.GetAllContents()
var/list/contents = AM.GetAllContents()
// We go backwards, so it'll be innermost objects sold first
for(var/i in reverseRange(contents))

View File

@@ -138,7 +138,7 @@ when portals are shortly lived, or when portals are made to be obvious with spec
/obj/effect/map_effect/portal/master/Initialize()
LAZYADD(all_portal_masters, src)
LAZYADD(listening_objects, src)
become_hearing_sensitive()
find_lines()
..()
return INITIALIZE_HINT_LATELOAD
@@ -150,7 +150,6 @@ when portals are shortly lived, or when portals are made to be obvious with spec
/obj/effect/map_effect/portal/master/Destroy()
LAZYREMOVE(all_portal_masters, src)
LAZYREMOVE(listening_objects, src)
for(var/thing in portal_lines)
qdel(thing)
return ..()
@@ -232,12 +231,9 @@ when portals are shortly lived, or when portals are made to be obvious with spec
if(!counterpart)
return
var/turf/T = counterpart.get_focused_turf()
var/list/mobs_to_relay = list()
var/list/objs = list()
get_mobs_or_objs_in_view(T, world.view, mobs_to_relay, objs)
var/list/mobs_to_relay = get_hearers_in_view(world.view, T)
for(var/thing in mobs_to_relay)
var/mob/mob = thing
for(var/mob/mob in mobs_to_relay)
var/rendered = span("message", "[text]")
mob.show_message(rendered)
@@ -249,12 +245,9 @@ when portals are shortly lived, or when portals are made to be obvious with spec
return
var/rendered = span("message", "[msg]")
var/turf/T = counterpart.get_focused_turf()
var/list/mobs_to_relay = list()
var/list/objs = list()
get_mobs_or_objs_in_view(T, world.view, mobs_to_relay, objs)
var/list/mobs_to_relay = get_hearers_in_view(world.view, T)
for(var/thing in mobs_to_relay)
var/mob/mob = thing
for(var/mob/mob in mobs_to_relay)
mob.show_message(rendered)
..()
@@ -264,12 +257,9 @@ when portals are shortly lived, or when portals are made to be obvious with spec
if(!counterpart)
return
var/turf/T = counterpart.get_focused_turf()
var/list/mobs_to_relay = list()
var/list/objs = list()
get_mobs_or_objs_in_view(T, world.view, mobs_to_relay, objs)
var/list/mobs_to_relay = get_hearers_in_view(world.view, T)
for(var/thing in mobs_to_relay)
var/mob/mob = thing
for(var/mob/mob in mobs_to_relay)
var/accent_icon = M.get_accent_icon(speaking, src)
var/name_used = M.GetVoice()
var/rendered = null

View File

@@ -8,5 +8,5 @@
to_chat(A, "<br>")
to_chat(A, "<span class='warning'><b>Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you-BZZT</b></span>")
to_chat(A, "<br>")
for(var/obj/machinery/telecomms/T in telecomms_list)
for(var/obj/machinery/telecomms/T in SSmachinery.all_telecomms)
T.emp_act(1)

View File

@@ -22,6 +22,6 @@
/datum/event/communications_blackout/start()
for(var/obj/machinery/telecomms/T in telecomms_list)
for(var/obj/machinery/telecomms/T in SSmachinery.all_telecomms)
if(T.z in affecting_z)
T.emp_act(1)

View File

@@ -87,8 +87,7 @@
target.add_ion_law(law)
target.show_laws()
if(message_servers)
for (var/obj/machinery/message_server/MS in message_servers)
for (var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms)
MS.spamfilter.Cut()
var/i
for (i = 1, i <= MS.spamfilter_limit, i++)

View File

@@ -20,8 +20,8 @@
Notifications will be sent as updates occur.<br>"
var/my_department = "[station_name()] firewall subroutines"
for(var/obj/machinery/message_server/MS in SSmachinery.processing)
if(!MS.active) continue
for(var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms)
if(!MS.use_power) continue
MS.send_rc_message("Executive Officer's Desk", my_department, message, "", "", 2)
@@ -61,6 +61,6 @@
var/my_department = "[station_name()] firewall subroutines"
for(var/obj/machinery/message_server/MS in SSmachinery.processing)
if(!MS.active) continue
for(var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms)
if(!MS.use_power) continue
MS.send_rc_message("Executive Officer's Desk", my_department, message, "", "", 2)

View File

@@ -50,7 +50,7 @@
if(areas && areas.len > 0)
var/my_department = "[station_name()] firewall subroutines"
var/rc_message = "An unknown malicious program has been detected in the [english_list(areaName)] lighting and airlock control systems at [worldtime2text()]. Systems will be fully compromised within approximately three minutes. Direct intervention is required immediately.<br>"
for(var/obj/machinery/message_server/MS in SSmachinery.processing)
for(var/obj/machinery/telecomms/message_server/MS in SSmachinery.all_telecomms)
MS.send_rc_message("Engineering", my_department, rc_message, "", "", 2)
for(var/mob/living/silicon/ai/A in player_list)
to_chat(A, "<span class='danger'>Malicious program detected in the [english_list(areaName)] lighting and airlock control systems by [my_department].</span>")

View File

@@ -320,7 +320,8 @@ em {font-style: normal; font-weight: bold;}
.centradio {color: #7272a7;}
.airadio {color: #ec00ec;}
.entradio {color: #cfcfcf;}
.shipradio {color: #8b4cd8;}
.hailradio {color: #8b4cd8;}
.shipradio {color: #738465;}
.secradio {color: #e21111;}
.engradio {color: #cc7b01;}

View File

@@ -317,7 +317,8 @@ em {font-style: normal;font-weight: bold;}
.centradio {color: #5C5C8A;}
.airadio {color: #FF00FF;}
.entradio {color: #bd893c;}
.shipradio {color: #7331c4;}
.hailradio {color: #7331c4;}
.shipradio {color: #738465;}
.secradio {color: #A30000;}
.engradio {color: #A66300;}

View File

@@ -286,7 +286,8 @@ em {font-style: normal; font-weight: bold;}
.centradio {color: #5C5C8A;}
.airadio {color: #FF00FF;}
.entradio {color: #bd893c;}
.shipradio {color: #7331c4;}
.hailradio {color: #7331c4;}
.shipradio {color: #738465;}
.secradio {color: #A30000;}
.engradio {color: #A66300;}

View File

@@ -21,7 +21,7 @@
UNSETEMPTY(pilots)
if(radio)
radio.on = (head?.radio && head.radio.is_functional() && get_cell())
radio.set_on(head?.radio && head.radio.is_functional() && get_cell())
if(camera)
camera.status = (head?.camera && head.camera.is_functional())

View File

@@ -459,10 +459,9 @@
/obj/item/integrated_circuit/input/microphone/Initialize()
. = ..()
listening_objects |= src
become_hearing_sensitive()
/obj/item/integrated_circuit/input/microphone/Destroy()
listening_objects -= src
return ..()
/obj/item/integrated_circuit/input/microphone/hear_talk(mob/living/M, msg, var/verb="says", datum/language/speaking=null)

View File

@@ -65,6 +65,8 @@ var/global/dmm_suite/preloader/_preloader = new
var/stored_index = 1
var/list/atoms_to_initialise = list()
var/has_expanded_world_maxx = FALSE
var/has_expanded_world_maxy = FALSE
while(dmmRegex.Find(tfile, stored_index))
stored_index = dmmRegex.next
@@ -104,6 +106,7 @@ var/global/dmm_suite/preloader/_preloader = new
continue
else
world.maxz = zcrd //create a new z_level if needed
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, world.maxz)
if(!no_changeturf)
WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/post_change is called.")
@@ -130,6 +133,7 @@ var/global/dmm_suite/preloader/_preloader = new
if(!cropMap && ycrd > world.maxy)
if(!measureOnly)
world.maxy = ycrd // Expand Y here. X is expanded in the loop below
has_expanded_world_maxy = TRUE
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], Clamp(ycrd, y_lower, y_upper))
else
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], Clamp(min(ycrd, world.maxy), y_lower, y_upper))
@@ -154,6 +158,7 @@ var/global/dmm_suite/preloader/_preloader = new
break
else
world.maxx = xcrd
has_expanded_world_maxx = TRUE
if(xcrd >= 1)
var/model_key = copytext(line, tpos, tpos + key_len)
@@ -182,10 +187,13 @@ var/global/dmm_suite/preloader/_preloader = new
else
if(!measureOnly)
if(!no_changeturf)
for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
var/turf/T = t
for(var/turf/T as anything in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
//we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
T.post_change(FALSE)
if(has_expanded_world_maxx || has_expanded_world_maxy)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPANDED_WORLD_BOUNDS, has_expanded_world_maxx, has_expanded_world_maxy)
var/datum/map_load_metadata/M = new
M.bounds = bounds
M.atoms_to_initialise = atoms_to_initialise

View File

@@ -325,7 +325,9 @@ swapmap
x2+=x1-1
y2+=y1-1
z2+=z1-1
world.maxz=max(z2,world.maxz) // stretch z if necessary
if(z2 > world.maxz)
world.maxz = z2 // stretch z if necessary
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, world.maxz)
if(!ischunk)
swapmaps_loaded[src]=null
swapmaps_byname[id]=src

View File

@@ -71,6 +71,9 @@ var/list/mineral_can_smooth_with = list(
turfs += src
if(isStationLevel(z))
station_turfs += src
if(dynamic_lighting)
luminosity = 0
else
@@ -197,6 +200,9 @@ var/list/mineral_can_smooth_with = list(
turfs += src
if(isStationLevel(z))
station_turfs += src
if(dynamic_lighting)
luminosity = 0
else
@@ -638,6 +644,9 @@ var/list/asteroid_floor_smooth = list(
turfs += src
if(isStationLevel(z))
station_turfs += src
if(dynamic_lighting)
luminosity = 0
else

View File

@@ -187,7 +187,7 @@
. = list()
var/turf/pos = get_turf(source)
if(pos)
for(var/turf/T in hear(range, pos))
for(var/turf/T in get_hear(range, pos))
. += T
#undef UPDATE_BUFFER

View File

@@ -127,7 +127,7 @@
if(vr_mob)
to_chat(vr_mob, "[time] [message]")
/mob/proc/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="")
/mob/proc/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/part_c, var/mob/speaker = null, var/hard_to_hear = 0)
if(!client && !vr_mob)
return
@@ -165,13 +165,8 @@
else
speaker_name = "Unknown"
if(istype(speaker, /mob/living/carbon/human))
var/mob/living/carbon/human/H = speaker
if(H.voice)
speaker_name = H.voice
if(vname)
speaker_name = vname
if(ishuman(speaker) && speaker.GetVoice() != real_name)
speaker_name = speaker.GetVoice()
if(hard_to_hear)
speaker_name = "Unknown"

View File

@@ -236,7 +236,7 @@
/obj/item/device/mmi/radio_enabled/Initialize()
. = ..()
radio = new(src)//Spawns a radio inside the MMI.
radio.broadcasting = TRUE//So it's broadcasting from the start.
radio.set_broadcasting(TRUE) //So it's broadcasting from the start.
//Allows the brain to toggle the radio functions.
/obj/item/device/mmi/radio_enabled/verb/Toggle_Broadcasting()
@@ -249,8 +249,8 @@
if(brainmob.stat)//Only the brainmob will trigger these so no further check is necessary.
to_chat(brainmob, "Can't do that while incapacitated or dead.")
radio.broadcasting = radio.broadcasting==1 ? 0 : 1
to_chat(brainmob, SPAN_NOTICE("Radio is [radio.broadcasting==1 ? "now" : "no longer"] broadcasting."))
radio.set_broadcasting(!radio.get_broadcasting())
to_chat(brainmob, SPAN_NOTICE("Radio is [radio.get_broadcasting() ? "now" : "no longer"] broadcasting."))
/obj/item/device/mmi/radio_enabled/verb/Toggle_Listening()
set name = "Toggle Listening"
@@ -262,8 +262,8 @@
if(brainmob.stat)
to_chat(brainmob, "Can't do that while incapacitated or dead.")
radio.listening = radio.listening==1 ? 0 : 1
to_chat(brainmob, SPAN_NOTICE("Radio is [radio.listening==1 ? "now" : "no longer"] receiving broadcast."))
radio.set_listening(!radio.get_listening())
to_chat(brainmob, SPAN_NOTICE("Radio is [radio.get_listening() ? "now" : "no longer"] receiving broadcast."))
/obj/item/device/mmi/emp_act(severity)
if(!brainmob)

View File

@@ -161,7 +161,7 @@
return headsets[headsets[1]]
return null
/mob/living/carbon/human/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, successful_radio, whisper, var/is_singing = FALSE)
/mob/living/carbon/human/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whisper, var/is_singing = FALSE)
if(!whisper && (paralysis || InStasis()))
whisper(message, speaking)
return TRUE
@@ -170,53 +170,48 @@
for(var/obj/item/device/radio/intercom/I in view(1))
I.add_fingerprint(src)
used_radios += I
if(I.talk_into(src, message, null, verb, speaking))
successful_radio += I
I.talk_into(src, message, null, verb, speaking)
if("headset")
var/obj/item/device/radio/R = get_radio()
if(R)
used_radios += R
if(R.talk_into(src, message, null, verb, speaking))
successful_radio += R
R.talk_into(src, message, null, verb, speaking)
if("right ear")
var/obj/item/device/radio/R
var/has_radio = 0
var/has_radio = FALSE
if(istype(r_ear,/obj/item/device/radio))
R = r_ear
has_radio = 1
has_radio = TRUE
if(istype(r_hand, /obj/item/device/radio))
R = r_hand
has_radio = 1
has_radio = TRUE
if(has_radio)
used_radios += R
if(R.talk_into(src,message,null,verb,speaking))
successful_radio += R
R.talk_into(src,message,null,verb,speaking)
if("left ear")
var/obj/item/device/radio/R
var/has_radio = 0
var/has_radio = FALSE
if(istype(l_ear, /obj/item/device/radio))
R = l_ear
has_radio = 1
has_radio = TRUE
if(istype(l_hand, /obj/item/device/radio))
R = l_hand
has_radio = 1
has_radio = TRUE
if(has_radio)
used_radios += R
if(R.talk_into(src,message,null,verb,speaking))
successful_radio += R
R.talk_into(src,message,null,verb,speaking)
if("wrist")
var/obj/item/device/radio/R
var/has_radio = 0
var/has_radio = FALSE
if(istype(wrists,/obj/item/device/radio))
R = wrists
has_radio = 1
has_radio = TRUE
if(istype(r_hand, /obj/item/device/radio))
R = wrists
has_radio = 1
has_radio = TRUE
if(has_radio)
used_radios += R
if(R.talk_into(src,message,null,verb,speaking))
successful_radio += R
R.talk_into(src,message,null,verb,speaking)
if("whisper")
whisper(message, speaking, is_singing)
return TRUE
@@ -224,8 +219,7 @@
var/obj/item/device/radio/R = get_radio()
if(R)
used_radios += R
if(R.talk_into(src, message, message_mode, verb, speaking))
successful_radio += R
R.talk_into(src, message, message_mode, verb, speaking)
/mob/living/carbon/human/handle_speech_sound()
var/list/returns = ..()

View File

@@ -16,7 +16,7 @@ var/list/department_radio_keys = list(
":x" = "Raider", ".x" = "Raider",
":b" = "Burglar", ".b" = "Burglar",
":j" = "Bluespace", ".j" = "Bluespace",
":y" = "Ship", ".y" = "Ship",
":y" = "Hailing", ".y" = "Hailing",
":q" = "Ninja", ".q" = "Ninja",
":u" = "Operations", ".u" = "Operations",
":v" = "Service", ".v" = "Service",
@@ -39,7 +39,7 @@ var/list/department_radio_keys = list(
":X" = "Raider", ".X" = "Raider",
":B" = "Burglar", ".B" = "Burglar",
":J" = "Bluespace", ".J" = "Bluespace",
":Y" = "Ship", ".Y" = "Ship",
":Y" = "Hailing", ".Y" = "Hailing",
":Q" = "Ninja", ".Q" = "Ninja",
":U" = "Operations", ".U" = "Operations",
":V" = "Service", ".V" = "Service",
@@ -129,16 +129,17 @@ proc/get_radio_key_from_channel(var/channel)
/mob/living/proc/get_stutter_verbs()
return list("stammers", "stutters")
/mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, successful_radio, whisper)
/mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whisper)
if(message_mode == "intercom")
for(var/obj/item/device/radio/intercom/I in view(1, null))
for(var/obj/item/device/radio/intercom/I in view(1, src))
used_radios += I
if(I.talk_into(src, message, verb, speaking))
successful_radio += I
I.talk_into(src, message, verb, speaking)
if(message_mode == "whisper" && !whisper)
whisper(message, speaking)
return TRUE
return 0
return FALSE
/mob/living/proc/handle_speech_sound()
var/list/returns[3]
@@ -250,34 +251,28 @@ proc/get_radio_key_from_channel(var/channel)
return say_signlang(message, pick(speaking.signlang_verb), speaking, speaking.sign_adv_length)
var/list/obj/item/used_radios = new
var/list/successful_radio = new // passes a list because standard vars don't work when passed
if(handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, successful_radio, whisper, is_singing))
return 1
if(handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whisper, is_singing))
return TRUE
var/list/handle_v = handle_speech_sound()
var/sound/speech_sound = handle_v[1]
var/sound_vol = handle_v[2]
var/italics = handle_v[3]
//speaking into radios
if(length(used_radios))
italics = 1
message_range = 1
if(speaking)
message_range = speaking.get_talkinto_msg_range(message)
var/msg
if(!speaking || !(speaking.flags & NO_TALK_MSG))
msg = "<span class='notice'>\The [src] [length(successful_radio) ? "talks into" : "tries talking into"] \the [used_radios[1]]</span>."
for(var/mob/living/M in hearers(5, src) - src)
if(msg)
M.show_message(msg)
var/msg = SPAN_NOTICE("\The [src] talks into \the [used_radios[1]].")
for (var/mob/living/L in get_hearers_in_view(5, src) - src)
L.show_message(msg)
if(speech_sound)
sound_vol *= 0.5
var/list/listening = list()
var/list/listening_obj = list()
var/turf/T = get_turf(src)
if(whisper)
@@ -296,14 +291,21 @@ proc/get_radio_key_from_channel(var/channel)
italics = 1
sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
get_mobs_or_objs_in_view(T, message_range, listening, listening_obj, ghost_hearing)
listening = get_hearers_in_view(message_range, src)
if(client)
for (var/mob/player_mob in player_list)
if(!player_mob || player_mob.stat != DEAD || (player_mob in listening))
continue
if(player_mob.client?.prefs.toggles & CHAT_GHOSTEARS)
listening |= player_mob
var/list/hear_clients = list()
for(var/m in listening)
var/mob/M = m
for(var/mob/M in listening)
var/heard_say = M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol, get_font_size_modifier())
if(heard_say && M.client)
hear_clients += M.client
listening -= M
var/speech_bubble_test = say_test(message)
var/image/speech_bubble = image(get_talk_bubble(),src,"h[speech_bubble_test]")
@@ -313,7 +315,7 @@ proc/get_radio_key_from_channel(var/channel)
var/bypass_listen_obj = (speaking && (speaking.flags & PASSLISTENOBJ))
if(!bypass_listen_obj)
for(var/obj/O as anything in listening_obj)
for(var/obj/O as anything in listening)
if(O) //It's possible that it could be deleted in the meantime.
INVOKE_ASYNC(O, /obj/.proc/hear_talk, src, message, verb, speaking)

View File

@@ -136,9 +136,9 @@
if(common_radio)
if(!is_component_functioning("radio"))
common_radio.on = FALSE
common_radio.set_on(FALSE)
else
common_radio.on = TRUE
common_radio.set_on(TRUE)
if(is_component_functioning("camera"))
blinded = FALSE

View File

@@ -20,7 +20,7 @@
returns[4] = world.view
return returns
/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, successful_radio, whisper)
/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whisper)
if(message_mode == "whisper" && !whisper)
whisper(message, speaking)
return TRUE
@@ -36,7 +36,7 @@
/mob/living/silicon/robot/drone/handle_message_mode()
return null
/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, successful_radio, whisper)
/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whisper)
if(message_mode == "whisper" && !whisper)
whisper(message, speaking)
return TRUE
@@ -52,7 +52,7 @@
log_say("[key_name(src)] : [message]",ckey=key_name(src))
return ai_radio.talk_into(src, message, message_mode, verb, speaking)
/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, successful_radio, whisper)
/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whisper)
if(message_mode)
if(message_mode == "whisper" && !whisper)
whisper(message, speaking)
@@ -120,7 +120,7 @@
var/turf/T = get_turf(H)
if(T)
var/list/hear = hear(7, T)
var/list/hear = get_hear(7, T)
var/list/hearturfs = list()
for(var/I in hear)

View File

@@ -339,7 +339,7 @@
/mob/living/simple_animal/spiderbot/get_bullet_impact_effect_type(var/def_zone)
return BULLET_IMPACT_METAL
/mob/living/simple_animal/spiderbot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, successful_radio, whisper)
/mob/living/simple_animal/spiderbot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whisper)
switch(message_mode)
if("whisper")
if(!whisper)

View File

@@ -72,7 +72,7 @@
/mob/living/simple_animal/hostile/hivebotbeacon/proc/generate_warp_destinations()
destinations.Cut()
for(var/turf/simulated/floor/T in circlerange(src,10))
for(var/turf/simulated/floor/T in circle_range(src,10))
if(turf_clear(T))
destinations += T
var/area/A = get_area(src)

Some files were not shown because too many files have changed in this diff Show More