"
-
-#define RDSCREEN_TEXT_NO_PROTOLATHE "No Protolathe Linked!
"
-#define RDSCREEN_TEXT_NO_IMPRINTER "No Circuit Imprinter Linked!
"
-#define RDSCREEN_TEXT_NO_DECONSTRUCT "No Destructive Analyzer Linked!
"
-#define RDSCREEN_TEXT_NO_TDISK "No Technology Disk Inserted!
"
-#define RDSCREEN_TEXT_NO_DDISK "No Design Disk Inserted!
"
-#define RDSCREEN_TEXT_NO_SNODE "No Technology Node Selected!
"
-#define RDSCREEN_TEXT_NO_SDESIGN "No Design Selected!
"
-
-#define RDSCREEN_UI_LATHE_CHECK if(QDELETED(linked_lathe)) { return RDSCREEN_TEXT_NO_PROTOLATHE }
-#define RDSCREEN_UI_IMPRINTER_CHECK if(QDELETED(linked_imprinter)) { return RDSCREEN_TEXT_NO_IMPRINTER }
-#define RDSCREEN_UI_DECONSTRUCT_CHECK if(QDELETED(linked_destroy)) { return RDSCREEN_TEXT_NO_DECONSTRUCT }
-#define RDSCREEN_UI_TDISK_CHECK if(QDELETED(t_disk)) { return RDSCREEN_TEXT_NO_TDISK }
-#define RDSCREEN_UI_DDISK_CHECK if(QDELETED(d_disk)) { return RDSCREEN_TEXT_NO_DDISK }
-#define RDSCREEN_UI_SNODE_CHECK if(!selected_node) { return RDSCREEN_TEXT_NO_SNODE }
-#define RDSCREEN_UI_SDESIGN_CHECK if(!selected_design) { return RDSCREEN_TEXT_NO_SDESIGN }
-
-#define RESEARCH_FABRICATOR_SCREEN_MAIN 1
-#define RESEARCH_FABRICATOR_SCREEN_CHEMICALS 2
-#define RESEARCH_FABRICATOR_SCREEN_MATERIALS 3
-#define RESEARCH_FABRICATOR_SCREEN_SEARCH 4
-#define RESEARCH_FABRICATOR_SCREEN_CATEGORYVIEW 5
-
-#define DEPARTMENTAL_FLAG_SECURITY (1<<0)
-#define DEPARTMENTAL_FLAG_MEDICAL (1<<1)
-#define DEPARTMENTAL_FLAG_CARGO (1<<2)
-#define DEPARTMENTAL_FLAG_SCIENCE (1<<3)
-#define DEPARTMENTAL_FLAG_ENGINEERING (1<<4)
-#define DEPARTMENTAL_FLAG_SERVICE (1<<5)
-#define DEPARTMENTAL_FLAG_ALL (1<<6) //NO THIS DOESN'T ALLOW YOU TO PRINT EVERYTHING, IT'S FOR ALL DEPARTMENTS!
-//#define DEPARTMENTAL_FLAG_MINING (1<<7)
-
-#define DESIGN_ID_IGNORE "IGNORE_THIS_DESIGN"
-
-#define RESEARCH_MATERIAL_RECLAMATION_ID "__materials"
-
-//When adding new types, update the list below!
-#define TECHWEB_POINT_TYPE_GENERIC "General Research"
-
-#define TECHWEB_POINT_TYPE_DEFAULT TECHWEB_POINT_TYPE_GENERIC
-
-//defined here so people don't forget to change this!
-#define TECHWEB_POINT_TYPE_LIST_ASSOCIATIVE_NAMES list(\
- TECHWEB_POINT_TYPE_GENERIC = "General Research"\
- )
-
-
-#define TOXINS_RESEARCH_MAX 70000 // This is the maximum amount of research points a toxins bomb can get.
-#define TOXINS_RESEARCH_LAMBDA 3940 // This determines how easy it is for a toxins bomb to reach the max research cap.
+
+#define RDCONSOLE_UI_MODE_NORMAL 1
+#define RDCONSOLE_UI_MODE_EXPERT 2
+#define RDCONSOLE_UI_MODE_LIST 3
+
+//RDSCREEN screens
+#define RDSCREEN_MENU 0
+#define RDSCREEN_TECHDISK 1
+#define RDSCREEN_DESIGNDISK 20
+#define RDSCREEN_DESIGNDISK_UPLOAD 21
+#define RDSCREEN_DECONSTRUCT 3
+#define RDSCREEN_PROTOLATHE 40
+#define RDSCREEN_PROTOLATHE_MATERIALS 41
+#define RDSCREEN_PROTOLATHE_CHEMICALS 42
+#define RDSCREEN_PROTOLATHE_CATEGORY_VIEW 43
+#define RDSCREEN_PROTOLATHE_SEARCH 44
+#define RDSCREEN_IMPRINTER 50
+#define RDSCREEN_IMPRINTER_MATERIALS 51
+#define RDSCREEN_IMPRINTER_CHEMICALS 52
+#define RDSCREEN_IMPRINTER_CATEGORY_VIEW 53
+#define RDSCREEN_IMPRINTER_SEARCH 54
+#define RDSCREEN_SETTINGS 61
+#define RDSCREEN_DEVICE_LINKING 62
+#define RDSCREEN_TECHWEB 70
+#define RDSCREEN_TECHWEB_NODEVIEW 71
+#define RDSCREEN_TECHWEB_DESIGNVIEW 72
+
+#define RDSCREEN_NOBREAK ""
+
+#define RDSCREEN_TEXT_NO_PROTOLATHE "No Protolathe Linked!
"
+#define RDSCREEN_TEXT_NO_IMPRINTER "No Circuit Imprinter Linked!
"
+#define RDSCREEN_TEXT_NO_DECONSTRUCT "No Destructive Analyzer Linked!
"
+#define RDSCREEN_TEXT_NO_TDISK "No Technology Disk Inserted!
"
+#define RDSCREEN_TEXT_NO_DDISK "No Design Disk Inserted!
"
+#define RDSCREEN_TEXT_NO_SNODE "No Technology Node Selected!
"
+#define RDSCREEN_TEXT_NO_SDESIGN "No Design Selected!
"
+
+#define RDSCREEN_UI_LATHE_CHECK if(QDELETED(linked_lathe)) { return RDSCREEN_TEXT_NO_PROTOLATHE }
+#define RDSCREEN_UI_IMPRINTER_CHECK if(QDELETED(linked_imprinter)) { return RDSCREEN_TEXT_NO_IMPRINTER }
+#define RDSCREEN_UI_DECONSTRUCT_CHECK if(QDELETED(linked_destroy)) { return RDSCREEN_TEXT_NO_DECONSTRUCT }
+#define RDSCREEN_UI_TDISK_CHECK if(QDELETED(t_disk)) { return RDSCREEN_TEXT_NO_TDISK }
+#define RDSCREEN_UI_DDISK_CHECK if(QDELETED(d_disk)) { return RDSCREEN_TEXT_NO_DDISK }
+#define RDSCREEN_UI_SNODE_CHECK if(!selected_node) { return RDSCREEN_TEXT_NO_SNODE }
+#define RDSCREEN_UI_SDESIGN_CHECK if(!selected_design) { return RDSCREEN_TEXT_NO_SDESIGN }
+
+#define RESEARCH_FABRICATOR_SCREEN_MAIN 1
+#define RESEARCH_FABRICATOR_SCREEN_CHEMICALS 2
+#define RESEARCH_FABRICATOR_SCREEN_MATERIALS 3
+#define RESEARCH_FABRICATOR_SCREEN_SEARCH 4
+#define RESEARCH_FABRICATOR_SCREEN_CATEGORYVIEW 5
+
+#define DEPARTMENTAL_FLAG_SECURITY (1<<0)
+#define DEPARTMENTAL_FLAG_MEDICAL (1<<1)
+#define DEPARTMENTAL_FLAG_CARGO (1<<2)
+#define DEPARTMENTAL_FLAG_SCIENCE (1<<3)
+#define DEPARTMENTAL_FLAG_ENGINEERING (1<<4)
+#define DEPARTMENTAL_FLAG_SERVICE (1<<5)
+#define DEPARTMENTAL_FLAG_ALL (1<<6) //NO THIS DOESN'T ALLOW YOU TO PRINT EVERYTHING, IT'S FOR ALL DEPARTMENTS!
+//#define DEPARTMENTAL_FLAG_MINING (1<<7)
+
+#define DESIGN_ID_IGNORE "IGNORE_THIS_DESIGN"
+
+#define RESEARCH_MATERIAL_RECLAMATION_ID "__materials"
+
+//When adding new types, update the list below!
+#define TECHWEB_POINT_TYPE_GENERIC "General Research"
+
+#define TECHWEB_POINT_TYPE_DEFAULT TECHWEB_POINT_TYPE_GENERIC
+
+//defined here so people don't forget to change this!
+#define TECHWEB_POINT_TYPE_LIST_ASSOCIATIVE_NAMES list(\
+ TECHWEB_POINT_TYPE_GENERIC = "General Research"\
+ )
+
+
+#define TOXINS_RESEARCH_MAX 70000 // This is the maximum amount of research points a toxins bomb can get.
+#define TOXINS_RESEARCH_LAMBDA 3940 // This determines how easy it is for a toxins bomb to reach the max research cap.
diff --git a/code/__DEFINES/sight.dm b/code/__DEFINES/sight.dm
index 6ab3092d1d04..5cac2900823b 100644
--- a/code/__DEFINES/sight.dm
+++ b/code/__DEFINES/sight.dm
@@ -1,35 +1,35 @@
-#define SEE_INVISIBLE_MINIMUM 5
-
-#define INVISIBILITY_LIGHTING 20
-
-#define SEE_INVISIBLE_LIVING 25
-
-//#define SEE_INVISIBLE_LEVEL_ONE 35 //currently unused
-//#define INVISIBILITY_LEVEL_ONE 35 //currently unused
-
-//#define SEE_INVISIBLE_LEVEL_TWO 45 //currently unused
-//#define INVISIBILITY_LEVEL_TWO 45 //currently unused
-
-#define INVISIBILITY_OBSERVER 60
-#define SEE_INVISIBLE_OBSERVER 60
-
-#define INVISIBILITY_MAXIMUM 100 //the maximum allowed for "real" objects
-
-#define INVISIBILITY_ABSTRACT 101 //only used for abstract objects (e.g. spacevine_controller), things that are not really there.
-
-#define BORGMESON (1<<0)
-#define BORGTHERM (1<<1)
-#define BORGXRAY (1<<2)
-#define BORGMATERIAL (1<<3)
-
-//for clothing visor toggles, these determine which vars to toggle
-#define VISOR_FLASHPROTECT (1<<0)
-#define VISOR_TINT (1<<1)
-#define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses
-#define VISOR_DARKNESSVIEW (1<<3)
-#define VISOR_INVISVIEW (1<<4)
-
-//for whether AI eyes see static, and whether it is mouse-opaque or not
-#define USE_STATIC_NONE 0
-#define USE_STATIC_TRANSPARENT 1
-#define USE_STATIC_OPAQUE 2
+#define SEE_INVISIBLE_MINIMUM 5
+
+#define INVISIBILITY_LIGHTING 20
+
+#define SEE_INVISIBLE_LIVING 25
+
+//#define SEE_INVISIBLE_LEVEL_ONE 35 //currently unused
+//#define INVISIBILITY_LEVEL_ONE 35 //currently unused
+
+//#define SEE_INVISIBLE_LEVEL_TWO 45 //currently unused
+//#define INVISIBILITY_LEVEL_TWO 45 //currently unused
+
+#define INVISIBILITY_OBSERVER 60
+#define SEE_INVISIBLE_OBSERVER 60
+
+#define INVISIBILITY_MAXIMUM 100 //the maximum allowed for "real" objects
+
+#define INVISIBILITY_ABSTRACT 101 //only used for abstract objects (e.g. spacevine_controller), things that are not really there.
+
+#define BORGMESON (1<<0)
+#define BORGTHERM (1<<1)
+#define BORGXRAY (1<<2)
+#define BORGMATERIAL (1<<3)
+
+//for clothing visor toggles, these determine which vars to toggle
+#define VISOR_FLASHPROTECT (1<<0)
+#define VISOR_TINT (1<<1)
+#define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses
+#define VISOR_DARKNESSVIEW (1<<3)
+#define VISOR_INVISVIEW (1<<4)
+
+//for whether AI eyes see static, and whether it is mouse-opaque or not
+#define USE_STATIC_NONE 0
+#define USE_STATIC_TRANSPARENT 1
+#define USE_STATIC_OPAQUE 2
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index 8c62bc636c34..64e60afb1288 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -1,75 +1,75 @@
-//max channel is 1024. Only go lower from here, because byond tends to pick the first availiable channel to play sounds on
-#define CHANNEL_LOBBYMUSIC 1024
-#define CHANNEL_ADMIN 1023
-#define CHANNEL_VOX 1022
-#define CHANNEL_JUKEBOX 1021
-#define CHANNEL_JUSTICAR_ARK 1020
-#define CHANNEL_HEARTBEAT 1019 //sound channel for heartbeats
-#define CHANNEL_AMBIENCE 1018
-#define CHANNEL_BUZZ 1017
-#define CHANNEL_BICYCLE 1016
-
-//THIS SHOULD ALWAYS BE THE LOWEST ONE!
-//KEEP IT UPDATED
-
-#define CHANNEL_HIGHEST_AVAILABLE 1015
-
-
-#define SOUND_MINIMUM_PRESSURE 10
-#define FALLOFF_SOUNDS 1
-
-
-//Ambience types
-
-#define GENERIC list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\
- 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\
- 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\
- 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\
- 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\
- 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg','sound/ambience/ambigen15.ogg')
-
-#define HOLY list('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg',\
- 'sound/ambience/ambicha4.ogg', 'sound/ambience/ambiholy.ogg', 'sound/ambience/ambiholy2.ogg',\
- 'sound/ambience/ambiholy3.ogg')
-
-#define HIGHSEC list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg')
-
-#define RUINS list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\
- 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
- 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
- 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambitech3.ogg',\
- 'sound/ambience/ambimystery.ogg', 'sound/ambience/ambimaint1.ogg')
-
-#define ENGINEERING list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg',\
- 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg')
-
-#define MINING list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\
- 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
- 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
- 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint1.ogg', 'sound/ambience/ambilava.ogg')
-
-#define MEDICAL list('sound/ambience/ambinice.ogg')
-
-#define SPOOKY list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/ambience/ambiruin7.ogg','sound/ambience/ambiruin6.ogg',\
- 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambimystery.ogg')
-
-#define SPACE list('sound/ambience/ambispace.ogg', 'sound/ambience/ambispace2.ogg', 'sound/ambience/title2.ogg', 'sound/ambience/ambiatmos.ogg')
-
-#define MAINTENANCE list('sound/ambience/ambimaint1.ogg', 'sound/ambience/ambimaint2.ogg', 'sound/ambience/ambimaint3.ogg', 'sound/ambience/ambimaint4.ogg',\
- 'sound/ambience/ambimaint5.ogg', 'sound/voice/lowHiss2.ogg', 'sound/voice/lowHiss3.ogg', 'sound/voice/lowHiss4.ogg', 'sound/ambience/ambitech2.ogg' )
-
-#define AWAY_MISSION list('sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiruin.ogg',\
- 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
- 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
- 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint.ogg',\
- 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambiodd.ogg')
-
-#define REEBE list('sound/ambience/ambireebe1.ogg', 'sound/ambience/ambireebe2.ogg', 'sound/ambience/ambireebe3.ogg')
-
-
-
-#define CREEPY_SOUNDS list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/screech.ogg',\
- 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\
- 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
- 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
- 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg')
+//max channel is 1024. Only go lower from here, because byond tends to pick the first availiable channel to play sounds on
+#define CHANNEL_LOBBYMUSIC 1024
+#define CHANNEL_ADMIN 1023
+#define CHANNEL_VOX 1022
+#define CHANNEL_JUKEBOX 1021
+#define CHANNEL_JUSTICAR_ARK 1020
+#define CHANNEL_HEARTBEAT 1019 //sound channel for heartbeats
+#define CHANNEL_AMBIENCE 1018
+#define CHANNEL_BUZZ 1017
+#define CHANNEL_BICYCLE 1016
+
+//THIS SHOULD ALWAYS BE THE LOWEST ONE!
+//KEEP IT UPDATED
+
+#define CHANNEL_HIGHEST_AVAILABLE 1015
+
+
+#define SOUND_MINIMUM_PRESSURE 10
+#define FALLOFF_SOUNDS 1
+
+
+//Ambience types
+
+#define GENERIC list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\
+ 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\
+ 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\
+ 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\
+ 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\
+ 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg','sound/ambience/ambigen15.ogg')
+
+#define HOLY list('sound/ambience/ambicha1.ogg','sound/ambience/ambicha2.ogg','sound/ambience/ambicha3.ogg',\
+ 'sound/ambience/ambicha4.ogg', 'sound/ambience/ambiholy.ogg', 'sound/ambience/ambiholy2.ogg',\
+ 'sound/ambience/ambiholy3.ogg')
+
+#define HIGHSEC list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg')
+
+#define RUINS list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\
+ 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
+ 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
+ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambitech3.ogg',\
+ 'sound/ambience/ambimystery.ogg', 'sound/ambience/ambimaint1.ogg')
+
+#define ENGINEERING list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg',\
+ 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg')
+
+#define MINING list('sound/ambience/ambimine.ogg', 'sound/ambience/ambicave.ogg', 'sound/ambience/ambiruin.ogg',\
+ 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
+ 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
+ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint1.ogg', 'sound/ambience/ambilava.ogg')
+
+#define MEDICAL list('sound/ambience/ambinice.ogg')
+
+#define SPOOKY list('sound/ambience/ambimo1.ogg','sound/ambience/ambimo2.ogg','sound/ambience/ambiruin7.ogg','sound/ambience/ambiruin6.ogg',\
+ 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambimystery.ogg')
+
+#define SPACE list('sound/ambience/ambispace.ogg', 'sound/ambience/ambispace2.ogg', 'sound/ambience/title2.ogg', 'sound/ambience/ambiatmos.ogg')
+
+#define MAINTENANCE list('sound/ambience/ambimaint1.ogg', 'sound/ambience/ambimaint2.ogg', 'sound/ambience/ambimaint3.ogg', 'sound/ambience/ambimaint4.ogg',\
+ 'sound/ambience/ambimaint5.ogg', 'sound/voice/lowHiss2.ogg', 'sound/voice/lowHiss3.ogg', 'sound/voice/lowHiss4.ogg', 'sound/ambience/ambitech2.ogg' )
+
+#define AWAY_MISSION list('sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiruin.ogg',\
+ 'sound/ambience/ambiruin2.ogg', 'sound/ambience/ambiruin3.ogg', 'sound/ambience/ambiruin4.ogg',\
+ 'sound/ambience/ambiruin5.ogg', 'sound/ambience/ambiruin6.ogg', 'sound/ambience/ambiruin7.ogg',\
+ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint.ogg',\
+ 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambiodd.ogg')
+
+#define REEBE list('sound/ambience/ambireebe1.ogg', 'sound/ambience/ambireebe2.ogg', 'sound/ambience/ambireebe3.ogg')
+
+
+
+#define CREEPY_SOUNDS list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/screech.ogg',\
+ 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\
+ 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\
+ 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\
+ 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg')
diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm
index b975aa1f89b4..2dade0825ff6 100644
--- a/code/__DEFINES/stat.dm
+++ b/code/__DEFINES/stat.dm
@@ -1,22 +1,22 @@
-/*
- Used with the various stat variables (mob, machines)
-*/
-
-//mob/var/stat things
-#define CONSCIOUS 0
-#define SOFT_CRIT 1
-#define UNCONSCIOUS 2
-#define DEAD 3
-
-//Maximum healthiness an individual can have
-#define MAX_SATIETY 600
-
-// bitflags for machine stat variable
-#define BROKEN (1<<0)
-#define NOPOWER (1<<1)
-#define MAINT (1<<2) // under maintaince
-#define EMPED (1<<3) // temporary broken by EMP pulse
-
-//ai power requirement defines
-#define POWER_REQ_ALL 1
-#define POWER_REQ_CLOCKCULT 2
+/*
+ Used with the various stat variables (mob, machines)
+*/
+
+//mob/var/stat things
+#define CONSCIOUS 0
+#define SOFT_CRIT 1
+#define UNCONSCIOUS 2
+#define DEAD 3
+
+//Maximum healthiness an individual can have
+#define MAX_SATIETY 600
+
+// bitflags for machine stat variable
+#define BROKEN (1<<0)
+#define NOPOWER (1<<1)
+#define MAINT (1<<2) // under maintaince
+#define EMPED (1<<3) // temporary broken by EMP pulse
+
+//ai power requirement defines
+#define POWER_REQ_ALL 1
+#define POWER_REQ_CLOCKCULT 2
diff --git a/code/__DEFINES/tgs.config.dm b/code/__DEFINES/tgs.config.dm
index 32243f7ad407..9f4f63a1fcc7 100644
--- a/code/__DEFINES/tgs.config.dm
+++ b/code/__DEFINES/tgs.config.dm
@@ -1,11 +1,11 @@
-#define TGS_EXTERNAL_CONFIGURATION
-#define TGS_V3_API
-#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
-#define TGS_READ_GLOBAL(Name) GLOB.##Name
-#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
-#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]")
-#define TGS_INFO_LOG(message) log_world("TGS: Info: [##message]")
-#define TGS_ERROR_LOG(message) log_world("TGS: Error: [##message]")
-#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
-#define TGS_CLIENT_COUNT GLOB.clients.len
-#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path)
+#define TGS_EXTERNAL_CONFIGURATION
+#define TGS_V3_API
+#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
+#define TGS_READ_GLOBAL(Name) GLOB.##Name
+#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
+#define TGS_WORLD_ANNOUNCE(message) to_chat(world, "[html_encode(##message)]")
+#define TGS_INFO_LOG(message) log_world("TGS: Info: [##message]")
+#define TGS_ERROR_LOG(message) log_world("TGS: Error: [##message]")
+#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
+#define TGS_CLIENT_COUNT GLOB.clients.len
+#define TGS_PROTECT_DATUM(Path) GENERAL_PROTECT_DATUM(##Path)
diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
index 45a7e46ce3b6..37dc08d241ac 100644
--- a/code/__DEFINES/tgs.dm
+++ b/code/__DEFINES/tgs.dm
@@ -1,258 +1,258 @@
-//tgstation-server DMAPI
-
-//All functions and datums outside this document are subject to change with any version and should not be relied on
-
-//CONFIGURATION
-
-//create this define if you want to do configuration outside of this file
-#ifndef TGS_EXTERNAL_CONFIGURATION
-
-//Comment this out once you've filled in the below
-#error TGS API unconfigured
-
-//Uncomment this if you wish to allow the game to interact with TGS 3
-//This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()()
-//#define TGS_V3_API
-
-//Required interfaces (fill in with your codebase equivalent):
-
-//create a global variable named `Name` and set it to `Value`
-//These globals must not be modifiable from anywhere outside of the server tools
-#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value)
-
-//Read the value in the global variable `Name`
-#define TGS_READ_GLOBAL(Name)
-
-//Set the value in the global variable `Name` to `Value`
-#define TGS_WRITE_GLOBAL(Name, Value)
-
-//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game priveledge escalation
-#define TGS_PROTECT_DATUM(Path)
-
-//display an announcement `message` from the server to all players
-#define TGS_WORLD_ANNOUNCE(message)
-
-//Notify current in-game administrators of a string `event`
-#define TGS_NOTIFY_ADMINS(event)
-
-//Write an info `message` to a server log
-#define TGS_INFO_LOG(message)
-
-//Write an error `message` to a server log
-#define TGS_ERROR_LOG(message)
-
-//Get the number of connected /clients
-#define TGS_CLIENT_COUNT
-
-#endif
-
-//EVENT CODES
-
-#define TGS_EVENT_PORT_SWAP -2 //before a port change is about to happen, extra parameter is new port
-#define TGS_EVENT_REBOOT_MODE_CHANGE -1 //before a reboot mode change, extras parameters are the current and new reboot mode enums
-
-//See the descriptions for these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs
-#define TGS_EVENT_REPO_RESET_ORIGIN 0
-#define TGS_EVENT_REPO_CHECKOUT 1
-#define TGS_EVENT_REPO_FETCH 2
-#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
-#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
-#define TGS_EVENT_BYOND_INSTALL_START 5
-#define TGS_EVENT_BYOND_INSTALL_FAIL 6
-#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
-#define TGS_EVENT_COMPILE_START 8
-#define TGS_EVENT_COMPILE_CANCELLED 9
-#define TGS_EVENT_COMPILE_FAILURE 10
-#define TGS_EVENT_COMPILE_COMPLETE 11
-#define TGS_EVENT_INSTANCE_AUTO_UPDATE_START 12
-#define TGS_EVENT_REPO_MERGE_CONFLICT 13
-
-//OTHER ENUMS
-
-#define TGS_REBOOT_MODE_NORMAL 0
-#define TGS_REBOOT_MODE_SHUTDOWN 1
-#define TGS_REBOOT_MODE_RESTART 2
-
-#define TGS_SECURITY_TRUSTED 0
-#define TGS_SECURITY_SAFE 1
-#define TGS_SECURITY_ULTRASAFE 2
-
-//REQUIRED HOOKS
-
-//Call this somewhere in /world/New() that is always run
-//event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels
-//minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated
-/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
- return
-
-//Call this when your initializations are complete and your game is ready to play before any player interactions happen
-//This may use world.sleep_offline to make this happen so ensure no changes are made to it while this call is running
-//Most importantly, before this point, note that any static files or directories may be in use by another server. Your code should account for this
-//This function should not be called before ..() in /world/New()
-/world/proc/TgsInitializationComplete()
- return
-
-//Put this at the start of /world/Topic()
-#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
-
-//Call this at the beginning of world/Reboot(reason)
-/world/proc/TgsReboot()
- return
-
-//DATUM DEFINITIONS
-//unless otherwise specified all datums defined here should be considered read-only, warranty void if written
-
-//represents git revision information about the current world build
-/datum/tgs_revision_information
- var/commit //full sha of compiled commit
- var/origin_commit //full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch
-
-//represents a version of tgstation-server
-/datum/tgs_version
- var/suite //The suite version, can be >=3
-
- //this group of variables can be null to represent a wild card
- var/major //The major version
- var/minor //The minor version
- var/patch //The patch version
-
- var/raw_parameter //The unparsed parameter
- var/deprefixed_parameter //The version only bit of raw_parameter
-
-//if the tgs_version is a wildcard version
-/datum/tgs_version/proc/Wildcard()
- return
-
-//represents a merge of a GitHub pull request
-/datum/tgs_revision_information/test_merge
- var/number //pull request number
- var/title //pull request title
- var/body //pull request body
- var/author //pull request github author
- var/url //link to pull request html
- var/pull_request_commit //commit of the pull request when it was merged
- var/time_merged //timestamp of when the merge commit for the pull request was created
- var/comment //optional comment left by the one who initiated the test merge
-
-//represents a connected chat channel
-/datum/tgs_chat_channel
- var/id //internal channel representation
- var/friendly_name //user friendly channel name
- var/connection_name //the name of the configured chat connection
- var/is_admin_channel //if the server operator has marked this channel for game admins only
- var/is_private_channel //if this is a private chat channel
- var/custom_tag //user defined string associated with channel
-
-//represents a chat user
-/datum/tgs_chat_user
- var/id //Internal user representation, requires channel to be unique
- var/friendly_name //The user's public name
- var/mention //The text to use to ping this user in a message
- var/datum/tgs_chat_channel/channel //The /datum/tgs_chat_channel this user was from
-
-//user definable callback for handling events
-//extra parameters may be specified depending on the event
-/datum/tgs_event_handler/proc/HandleEvent(event_code, ...)
- set waitfor = FALSE
- return
-
-//user definable chat command
-/datum/tgs_chat_command
- var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command
- var/help_text = "" //help text for this command
- var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins
-
-//override to implement command
-//sender: The tgs_chat_user who send to command
-//params: The trimmed string following the command name
-//The return value will be stringified and sent to the appropriate chat
-/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
- CRASH("[type] has no implementation for Run()")
-
-//FUNCTIONS
-
-//Returns the respective supported /datum/tgs_version of the API
-/world/proc/TgsMaximumAPIVersion()
- return
-
-/world/proc/TgsMinimumAPIVersion()
- return
-
-//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
-//No function below this succeeds if it returns FALSE
-/world/proc/TgsAvailable()
- return
-
-//Gets the current /datum/tgs_version of the server tools running the server
-/world/proc/TgsVersion()
- return
-
-/world/proc/TgsInstanceName()
- return
-
-//Get the current `/datum/tgs_revision_information`
-/world/proc/TgsRevision()
- return
-
-//Get the current BYOND security level
-/world/proc/TgsSecurityLevel()
- return
-
-//Gets a list of active `/datum/tgs_revision_information/test_merge`s
-/world/proc/TgsTestMerges()
- return
-
-//Forces a hard reboot of BYOND by ending the process
-//unlike del(world) clients will try to reconnect
-//If the service has not requested a shutdown, the next server will take over
-/world/proc/TgsEndProcess()
- return
-
-//Gets a list of connected tgs_chat_channel
-/world/proc/TgsChatChannelInfo()
- return
-
-//Sends a message to connected game chats
-//message: The message to send
-//channels: optional channels to limit the broadcast to
-/world/proc/TgsChatBroadcast(message, list/channels)
- return
-
-//Send a message to non-admin connected chats
-//message: The message to send
-//admin_only: If TRUE, message will instead be sent to only admin connected chats
-/world/proc/TgsTargetedChatBroadcast(message, admin_only)
- return
-
-//Send a private message to a specific user
-//message: The message to send
-//user: The /datum/tgs_chat_user to send to
-/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user)
- return
-
-/*
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+//tgstation-server DMAPI
+
+//All functions and datums outside this document are subject to change with any version and should not be relied on
+
+//CONFIGURATION
+
+//create this define if you want to do configuration outside of this file
+#ifndef TGS_EXTERNAL_CONFIGURATION
+
+//Comment this out once you've filled in the below
+#error TGS API unconfigured
+
+//Uncomment this if you wish to allow the game to interact with TGS 3
+//This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()()
+//#define TGS_V3_API
+
+//Required interfaces (fill in with your codebase equivalent):
+
+//create a global variable named `Name` and set it to `Value`
+//These globals must not be modifiable from anywhere outside of the server tools
+#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value)
+
+//Read the value in the global variable `Name`
+#define TGS_READ_GLOBAL(Name)
+
+//Set the value in the global variable `Name` to `Value`
+#define TGS_WRITE_GLOBAL(Name, Value)
+
+//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game priveledge escalation
+#define TGS_PROTECT_DATUM(Path)
+
+//display an announcement `message` from the server to all players
+#define TGS_WORLD_ANNOUNCE(message)
+
+//Notify current in-game administrators of a string `event`
+#define TGS_NOTIFY_ADMINS(event)
+
+//Write an info `message` to a server log
+#define TGS_INFO_LOG(message)
+
+//Write an error `message` to a server log
+#define TGS_ERROR_LOG(message)
+
+//Get the number of connected /clients
+#define TGS_CLIENT_COUNT
+
+#endif
+
+//EVENT CODES
+
+#define TGS_EVENT_PORT_SWAP -2 //before a port change is about to happen, extra parameter is new port
+#define TGS_EVENT_REBOOT_MODE_CHANGE -1 //before a reboot mode change, extras parameters are the current and new reboot mode enums
+
+//See the descriptions for these codes here: https://github.com/tgstation/tgstation-server/blob/master/src/Tgstation.Server.Host/Components/EventType.cs
+#define TGS_EVENT_REPO_RESET_ORIGIN 0
+#define TGS_EVENT_REPO_CHECKOUT 1
+#define TGS_EVENT_REPO_FETCH 2
+#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
+#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
+#define TGS_EVENT_BYOND_INSTALL_START 5
+#define TGS_EVENT_BYOND_INSTALL_FAIL 6
+#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
+#define TGS_EVENT_COMPILE_START 8
+#define TGS_EVENT_COMPILE_CANCELLED 9
+#define TGS_EVENT_COMPILE_FAILURE 10
+#define TGS_EVENT_COMPILE_COMPLETE 11
+#define TGS_EVENT_INSTANCE_AUTO_UPDATE_START 12
+#define TGS_EVENT_REPO_MERGE_CONFLICT 13
+
+//OTHER ENUMS
+
+#define TGS_REBOOT_MODE_NORMAL 0
+#define TGS_REBOOT_MODE_SHUTDOWN 1
+#define TGS_REBOOT_MODE_RESTART 2
+
+#define TGS_SECURITY_TRUSTED 0
+#define TGS_SECURITY_SAFE 1
+#define TGS_SECURITY_ULTRASAFE 2
+
+//REQUIRED HOOKS
+
+//Call this somewhere in /world/New() that is always run
+//event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels
+//minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated
+/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
+ return
+
+//Call this when your initializations are complete and your game is ready to play before any player interactions happen
+//This may use world.sleep_offline to make this happen so ensure no changes are made to it while this call is running
+//Most importantly, before this point, note that any static files or directories may be in use by another server. Your code should account for this
+//This function should not be called before ..() in /world/New()
+/world/proc/TgsInitializationComplete()
+ return
+
+//Put this at the start of /world/Topic()
+#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
+
+//Call this at the beginning of world/Reboot(reason)
+/world/proc/TgsReboot()
+ return
+
+//DATUM DEFINITIONS
+//unless otherwise specified all datums defined here should be considered read-only, warranty void if written
+
+//represents git revision information about the current world build
+/datum/tgs_revision_information
+ var/commit //full sha of compiled commit
+ var/origin_commit //full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch
+
+//represents a version of tgstation-server
+/datum/tgs_version
+ var/suite //The suite version, can be >=3
+
+ //this group of variables can be null to represent a wild card
+ var/major //The major version
+ var/minor //The minor version
+ var/patch //The patch version
+
+ var/raw_parameter //The unparsed parameter
+ var/deprefixed_parameter //The version only bit of raw_parameter
+
+//if the tgs_version is a wildcard version
+/datum/tgs_version/proc/Wildcard()
+ return
+
+//represents a merge of a GitHub pull request
+/datum/tgs_revision_information/test_merge
+ var/number //pull request number
+ var/title //pull request title
+ var/body //pull request body
+ var/author //pull request github author
+ var/url //link to pull request html
+ var/pull_request_commit //commit of the pull request when it was merged
+ var/time_merged //timestamp of when the merge commit for the pull request was created
+ var/comment //optional comment left by the one who initiated the test merge
+
+//represents a connected chat channel
+/datum/tgs_chat_channel
+ var/id //internal channel representation
+ var/friendly_name //user friendly channel name
+ var/connection_name //the name of the configured chat connection
+ var/is_admin_channel //if the server operator has marked this channel for game admins only
+ var/is_private_channel //if this is a private chat channel
+ var/custom_tag //user defined string associated with channel
+
+//represents a chat user
+/datum/tgs_chat_user
+ var/id //Internal user representation, requires channel to be unique
+ var/friendly_name //The user's public name
+ var/mention //The text to use to ping this user in a message
+ var/datum/tgs_chat_channel/channel //The /datum/tgs_chat_channel this user was from
+
+//user definable callback for handling events
+//extra parameters may be specified depending on the event
+/datum/tgs_event_handler/proc/HandleEvent(event_code, ...)
+ set waitfor = FALSE
+ return
+
+//user definable chat command
+/datum/tgs_chat_command
+ var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command
+ var/help_text = "" //help text for this command
+ var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins
+
+//override to implement command
+//sender: The tgs_chat_user who send to command
+//params: The trimmed string following the command name
+//The return value will be stringified and sent to the appropriate chat
+/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
+ CRASH("[type] has no implementation for Run()")
+
+//FUNCTIONS
+
+//Returns the respective supported /datum/tgs_version of the API
+/world/proc/TgsMaximumAPIVersion()
+ return
+
+/world/proc/TgsMinimumAPIVersion()
+ return
+
+//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
+//No function below this succeeds if it returns FALSE
+/world/proc/TgsAvailable()
+ return
+
+//Gets the current /datum/tgs_version of the server tools running the server
+/world/proc/TgsVersion()
+ return
+
+/world/proc/TgsInstanceName()
+ return
+
+//Get the current `/datum/tgs_revision_information`
+/world/proc/TgsRevision()
+ return
+
+//Get the current BYOND security level
+/world/proc/TgsSecurityLevel()
+ return
+
+//Gets a list of active `/datum/tgs_revision_information/test_merge`s
+/world/proc/TgsTestMerges()
+ return
+
+//Forces a hard reboot of BYOND by ending the process
+//unlike del(world) clients will try to reconnect
+//If the service has not requested a shutdown, the next server will take over
+/world/proc/TgsEndProcess()
+ return
+
+//Gets a list of connected tgs_chat_channel
+/world/proc/TgsChatChannelInfo()
+ return
+
+//Sends a message to connected game chats
+//message: The message to send
+//channels: optional channels to limit the broadcast to
+/world/proc/TgsChatBroadcast(message, list/channels)
+ return
+
+//Send a message to non-admin connected chats
+//message: The message to send
+//admin_only: If TRUE, message will instead be sent to only admin connected chats
+/world/proc/TgsTargetedChatBroadcast(message, admin_only)
+ return
+
+//Send a private message to a specific user
+//message: The message to send
+//user: The /datum/tgs_chat_user to send to
+/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user)
+ return
+
+/*
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm
index 878d35608d7c..f37116f060f6 100644
--- a/code/__DEFINES/tools.dm
+++ b/code/__DEFINES/tools.dm
@@ -1,15 +1,15 @@
-// Tool types
-#define TOOL_CROWBAR "crowbar"
-#define TOOL_MULTITOOL "multitool"
-#define TOOL_SCREWDRIVER "screwdriver"
-#define TOOL_WIRECUTTER "wirecutter"
-#define TOOL_WRENCH "wrench"
-#define TOOL_WELDER "welder"
-#define TOOL_ANALYZER "analyzer"
-#define TOOL_MINING "mining"
-#define TOOL_SHOVEL "shovel"
-
-
-// If delay between the start and the end of tool operation is less than MIN_TOOL_SOUND_DELAY,
-// tool sound is only played when op is started. If not, it's played twice.
-#define MIN_TOOL_SOUND_DELAY 20
+// Tool types
+#define TOOL_CROWBAR "crowbar"
+#define TOOL_MULTITOOL "multitool"
+#define TOOL_SCREWDRIVER "screwdriver"
+#define TOOL_WIRECUTTER "wirecutter"
+#define TOOL_WRENCH "wrench"
+#define TOOL_WELDER "welder"
+#define TOOL_ANALYZER "analyzer"
+#define TOOL_MINING "mining"
+#define TOOL_SHOVEL "shovel"
+
+
+// If delay between the start and the end of tool operation is less than MIN_TOOL_SOUND_DELAY,
+// tool sound is only played when op is started. If not, it's played twice.
+#define MIN_TOOL_SOUND_DELAY 20
diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm
index 960d0479bee8..34063c1787bc 100644
--- a/code/__DEFINES/wires.dm
+++ b/code/__DEFINES/wires.dm
@@ -1,50 +1,50 @@
-//retvals for attempt_wires_interaction
-#define WIRE_INTERACTION_FAIL 0
-#define WIRE_INTERACTION_SUCCESSFUL 1
-#define WIRE_INTERACTION_BLOCK 2 //don't do anything else rather than open wires and whatever else.
-
-#define WIRE_DUD_PREFIX "__dud"
-#define WIRE_ACTIVATE "Activate"
-#define WIRE_AI "AI Connection"
-#define WIRE_ALARM "Alarm"
-#define WIRE_AVOIDANCE "Avoidance"
-#define WIRE_BACKUP1 "Auxiliary Power 1"
-#define WIRE_BACKUP2 "Auxiliary Power 2"
-#define WIRE_BEACON "Beacon"
-#define WIRE_BOLTS "Bolts"
-#define WIRE_BOOM "Boom"
-#define WIRE_CAMERA "Camera"
-#define WIRE_CONTRABAND "Contraband"
-#define WIRE_DELAY "Delay"
-#define WIRE_DISABLE "Disable"
-#define WIRE_DISARM "Disarm"
-#define WIRE_HACK "Hack"
-#define WIRE_IDSCAN "ID Scan"
-#define WIRE_INTERFACE "Interface"
-#define WIRE_LAWSYNC "AI Law Synchronization"
-#define WIRE_LIGHT "Bolt Lights"
-#define WIRE_LIMIT "Limiter"
-#define WIRE_LOADCHECK "Load Check"
-#define WIRE_LOCKDOWN "Lockdown"
-#define WIRE_MOTOR1 "Motor 1"
-#define WIRE_MOTOR2 "Motor 2"
-#define WIRE_OPEN "Open"
-#define WIRE_PANIC "Panic Siphon"
-#define WIRE_POWER "Power"
-#define WIRE_POWER1 "Main Power 1"
-#define WIRE_POWER2 "Main Power 2"
-#define WIRE_PROCEED "Proceed"
-#define WIRE_RX "Receive"
-#define WIRE_RESET_MODULE "Reset Module"
-#define WIRE_SAFETY "Safety"
-#define WIRE_SHOCK "High Voltage Ground"
-#define WIRE_SIGNAL "Signal"
-#define WIRE_SPEAKER "Speaker"
-#define WIRE_STRENGTH "Strength"
-#define WIRE_THROW "Throw"
-#define WIRE_TIMING "Timing"
-#define WIRE_TX "Transmit"
-#define WIRE_UNBOLT "Unbolt"
-#define WIRE_ZAP "High Voltage Circuit"
-#define WIRE_ZAP1 "High Voltage Circuit 1"
-#define WIRE_ZAP2 "High Voltage Circuit 2"
+//retvals for attempt_wires_interaction
+#define WIRE_INTERACTION_FAIL 0
+#define WIRE_INTERACTION_SUCCESSFUL 1
+#define WIRE_INTERACTION_BLOCK 2 //don't do anything else rather than open wires and whatever else.
+
+#define WIRE_DUD_PREFIX "__dud"
+#define WIRE_ACTIVATE "Activate"
+#define WIRE_AI "AI Connection"
+#define WIRE_ALARM "Alarm"
+#define WIRE_AVOIDANCE "Avoidance"
+#define WIRE_BACKUP1 "Auxiliary Power 1"
+#define WIRE_BACKUP2 "Auxiliary Power 2"
+#define WIRE_BEACON "Beacon"
+#define WIRE_BOLTS "Bolts"
+#define WIRE_BOOM "Boom"
+#define WIRE_CAMERA "Camera"
+#define WIRE_CONTRABAND "Contraband"
+#define WIRE_DELAY "Delay"
+#define WIRE_DISABLE "Disable"
+#define WIRE_DISARM "Disarm"
+#define WIRE_HACK "Hack"
+#define WIRE_IDSCAN "ID Scan"
+#define WIRE_INTERFACE "Interface"
+#define WIRE_LAWSYNC "AI Law Synchronization"
+#define WIRE_LIGHT "Bolt Lights"
+#define WIRE_LIMIT "Limiter"
+#define WIRE_LOADCHECK "Load Check"
+#define WIRE_LOCKDOWN "Lockdown"
+#define WIRE_MOTOR1 "Motor 1"
+#define WIRE_MOTOR2 "Motor 2"
+#define WIRE_OPEN "Open"
+#define WIRE_PANIC "Panic Siphon"
+#define WIRE_POWER "Power"
+#define WIRE_POWER1 "Main Power 1"
+#define WIRE_POWER2 "Main Power 2"
+#define WIRE_PROCEED "Proceed"
+#define WIRE_RX "Receive"
+#define WIRE_RESET_MODULE "Reset Module"
+#define WIRE_SAFETY "Safety"
+#define WIRE_SHOCK "High Voltage Ground"
+#define WIRE_SIGNAL "Signal"
+#define WIRE_SPEAKER "Speaker"
+#define WIRE_STRENGTH "Strength"
+#define WIRE_THROW "Throw"
+#define WIRE_TIMING "Timing"
+#define WIRE_TX "Transmit"
+#define WIRE_UNBOLT "Unbolt"
+#define WIRE_ZAP "High Voltage Circuit"
+#define WIRE_ZAP1 "High Voltage Circuit 1"
+#define WIRE_ZAP2 "High Voltage Circuit 2"
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index 47659a55f969..64c014e8bf8f 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -1,589 +1,589 @@
-/*
- * Holds procs to help with list operations
- * Contains groups:
- * Misc
- * Sorting
- */
-
-/*
- * Misc
- */
-
-#define LAZYINITLIST(L) if (!L) L = list()
-#define UNSETEMPTY(L) if (L && !length(L)) L = null
-#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
-#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
-#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
-#define LAZYFIND(L, V) L ? L.Find(V) : 0
-#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
-#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
-#define LAZYLEN(L) length(L)
-#define LAZYCLEARLIST(L) if(L) L.Cut()
-#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
-#define reverseList(L) reverseRange(L.Copy())
-
-// binary search sorted insert
-// IN: Object to be inserted
-// LIST: List to insert object into
-// TYPECONT: The typepath of the contents of the list
-// COMPARE: The variable on the objects to compare
-#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \
- var/__BIN_CTTL = length(LIST);\
- if(!__BIN_CTTL) {\
- LIST += IN;\
- } else {\
- var/__BIN_LEFT = 1;\
- var/__BIN_RIGHT = __BIN_CTTL;\
- var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
- var/##TYPECONT/__BIN_ITEM;\
- while(__BIN_LEFT < __BIN_RIGHT) {\
- __BIN_ITEM = LIST[__BIN_MID];\
- if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\
- __BIN_LEFT = __BIN_MID + 1;\
- } else {\
- __BIN_RIGHT = __BIN_MID;\
- };\
- __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
- };\
- __BIN_ITEM = LIST[__BIN_MID];\
- __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\
- LIST.Insert(__BIN_MID, IN);\
- }
-
-//Returns a list in plain english as a string
-/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
- var/total = input.len
- if (!total)
- return "[nothing_text]"
- else if (total == 1)
- return "[input[1]]"
- else if (total == 2)
- return "[input[1]][and_text][input[2]]"
- else
- var/output = ""
- var/index = 1
- while (index < total)
- if (index == total - 1)
- comma_text = final_comma_text
-
- output += "[input[index]][comma_text]"
- index++
-
- return "[output][and_text][input[index]]"
-
-//Returns list element or null. Should prevent "index out of bounds" error.
-/proc/listgetindex(list/L, index)
- if(LAZYLEN(L))
- if(isnum(index) && ISINTEGER(index))
- if(ISINRANGE(index,1,L.len))
- return L[index]
- else if(index in L)
- return L[index]
- return
-
-//Return either pick(list) or null if list is not of type /list or is empty
-/proc/safepick(list/L)
- if(LAZYLEN(L))
- return pick(L)
-
-//Checks if the list is empty
-/proc/isemptylist(list/L)
- if(!L.len)
- return TRUE
- return FALSE
-
-//Checks for specific types in a list
-/proc/is_type_in_list(atom/A, list/L)
- if(!LAZYLEN(L) || !A)
- return FALSE
- for(var/type in L)
- if(istype(A, type))
- return TRUE
- return FALSE
-
-//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
-#define is_type_in_typecache(A, L) (A && length(L) && L[(ispath(A) ? A : A:type)])
-
-//Checks for a string in a list
-/proc/is_string_in_list(string, list/L)
- if(!LAZYLEN(L) || !string)
- return
- for(var/V in L)
- if(string == V)
- return TRUE
- return
-
-//Removes a string from a list
-/proc/remove_strings_from_list(string, list/L)
- if(!LAZYLEN(L) || !string)
- return
- for(var/V in L)
- if(V == string)
- L -= V //No return here so that it removes all strings of that type
- return
-
-//returns a new list with only atoms that are in typecache L
-/proc/typecache_filter_list(list/atoms, list/typecache)
- RETURN_TYPE(/list)
- . = list()
- for(var/thing in atoms)
- var/atom/A = thing
- if (typecache[A.type])
- . += A
-
-/proc/typecache_filter_list_reverse(list/atoms, list/typecache)
- RETURN_TYPE(/list)
- . = list()
- for(var/thing in atoms)
- var/atom/A = thing
- if(!typecache[A.type])
- . += A
-
-/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude)
- . = list()
- for(var/thing in atoms)
- var/atom/A = thing
- if(typecache_include[A.type] && !typecache_exclude[A.type])
- . += A
-
-//Like typesof() or subtypesof(), but returns a typecache instead of a list
-/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
- if(ispath(path))
- var/list/types = list()
- if(only_root_path)
- types = list(path)
- else
- types = ignore_root_path ? subtypesof(path) : typesof(path)
- var/list/L = list()
- for(var/T in types)
- L[T] = TRUE
- return L
- else if(islist(path))
- var/list/pathlist = path
- var/list/L = list()
- if(ignore_root_path)
- for(var/P in pathlist)
- for(var/T in subtypesof(P))
- L[T] = TRUE
- else
- for(var/P in pathlist)
- if(only_root_path)
- L[P] = TRUE
- else
- for(var/T in typesof(P))
- L[T] = TRUE
- return L
-
-//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
-/proc/clearlist(list/list)
- if(istype(list))
- list.len = 0
- return
-
-//Removes any null entries from the list
-//Returns TRUE if the list had nulls, FALSE otherwise
-/proc/listclearnulls(list/L)
- var/start_len = L.len
- var/list/N = new(start_len)
- L -= N
- return L.len < start_len
-
-/*
- * Returns list containing all the entries from first list that are not present in second.
- * If skiprep = 1, repeated elements are treated as one.
- * If either of arguments is not a list, returns null
- */
-/proc/difflist(list/first, list/second, skiprep=0)
- if(!islist(first) || !islist(second))
- return
- var/list/result = new
- if(skiprep)
- for(var/e in first)
- if(!(e in result) && !(e in second))
- result += e
- else
- result = first - second
- return result
-
-/*
- * Returns list containing entries that are in either list but not both.
- * If skipref = 1, repeated elements are treated as one.
- * If either of arguments is not a list, returns null
- */
-/proc/uniquemergelist(list/first, list/second, skiprep=0)
- if(!islist(first) || !islist(second))
- return
- var/list/result = new
- if(skiprep)
- result = difflist(first, second, skiprep)+difflist(second, first, skiprep)
- else
- result = first ^ second
- return result
-
-//Picks a random element from a list based on a weighting system:
-//1. Adds up the total of weights for each element
-//2. Gets a number between 1 and that total
-//3. For each element in the list, subtracts its weighting from that number
-//4. If that makes the number 0 or less, return that element.
-/proc/pickweight(list/L)
- var/total = 0
- var/item
- for (item in L)
- if (!L[item])
- L[item] = 1
- total += L[item]
-
- total *= rand() // Yogs -- Allows for noninteger weights
- for (item in L)
- total -=L [item]
- if (total <= 0)
- return item
-
- return null
-
-/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be.
- var/total = 0
- var/item
- for (item in L)
- if (!L[item])
- L[item] = 0
- total += L[item]
-
- total = rand(0, total)
- for (item in L)
- total -=L [item]
- if (total <= 0 && L[item])
- return item
-
- return null
-
-//Pick a random element from the list and remove it from the list.
-/proc/pick_n_take(list/L)
- RETURN_TYPE(L[_].type)
- if(L.len)
- var/picked = rand(1,L.len)
- . = L[picked]
- L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
-
-//Returns the top(last) element from the list and removes it from the list (typical stack function)
-/proc/pop(list/L)
- if(L.len)
- . = L[L.len]
- L.len--
-
-/proc/popleft(list/L)
- if(L.len)
- . = L[1]
- L.Cut(1,2)
-
-/proc/sorted_insert(list/L, thing, comparator)
- var/pos = L.len
- while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
- pos--
- L.Insert(pos+1, thing)
-
-// Returns the next item in a list
-/proc/next_list_item(item, list/L)
- var/i
- i = L.Find(item)
- if(i == L.len)
- i = 1
- else
- i++
- return L[i]
-
-// Returns the previous item in a list
-/proc/previous_list_item(item, list/L)
- var/i
- i = L.Find(item)
- if(i == 1)
- i = L.len
- else
- i--
- return L[i]
-
-//Randomize: Return the list in a random order
-/proc/shuffle(list/L)
- if(!L)
- return
- L = L.Copy()
-
- for(var/i=1, i= 0 ? /proc/cmp_ckey_asc : /proc/cmp_ckey_dsc)
-
-//Specifically for record datums in a list.
-/proc/sortRecord(list/L, field = "name", order = 1)
- GLOB.cmp_field = field
- return sortTim(L, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc)
-
-//any value in a list
-/proc/sortList(list/L, cmp=/proc/cmp_text_asc)
- return sortTim(L.Copy(), cmp)
-
-//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
-/proc/sortNames(list/L, order=1)
- return sortTim(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
-
-
-//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
-/proc/bitfield2list(bitfield = 0, list/wordlist)
- var/list/r = list()
- if(islist(wordlist))
- var/max = min(wordlist.len,16)
- var/bit = 1
- for(var/i=1, i<=max, i++)
- if(bitfield & bit)
- r += wordlist[i]
- bit = bit << 1
- else
- for(var/bit=1, bit<=65535, bit = bit << 1)
- if(bitfield & bit)
- r += bit
-
- return r
-
-// Returns the key based on the index
-#define KEYBYINDEX(L, index) (((index <= length(L)) && (index > 0)) ? L[index] : null)
-
-/proc/count_by_type(list/L, type)
- var/i = 0
- for(var/T in L)
- if(istype(T, type))
- i++
- return i
-
-/proc/find_record(field, value, list/L)
- for(var/datum/data/record/R in L)
- if(R.fields[field] == value)
- return R
-
-
-//Move a single element from position fromIndex within a list, to position toIndex
-//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
-//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
-//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
-//fromIndex and toIndex must be in the range [1,L.len+1]
-//This will preserve associations ~Carnie
-/proc/moveElement(list/L, fromIndex, toIndex)
- if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
- return
- if(fromIndex > toIndex)
- ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
-
- L.Insert(toIndex, null)
- L.Swap(fromIndex, toIndex)
- L.Cut(fromIndex, fromIndex+1)
-
-
-//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
-//Same as moveElement but for ranges of elements
-//This will preserve associations ~Carnie
-/proc/moveRange(list/L, fromIndex, toIndex, len=1)
- var/distance = abs(toIndex - fromIndex)
- if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
- if(fromIndex <= toIndex)
- return //no need to move
- fromIndex += len //we want to shift left instead of right
-
- for(var/i=0, i toIndex)
- fromIndex += len
-
- for(var/i=0, i distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
- if(fromIndex < toIndex)
- toIndex += len
- else
- fromIndex += len
-
- for(var/i=0, i fromIndex)
- var/a = toIndex
- toIndex = fromIndex
- fromIndex = a
-
- for(var/i=0, i 512
-#error Remie said that lummox was adding a way to get a lists
-#error contents via list.values, if that is true remove this
-#error otherwise, update the version and bug lummox
-#endif
-//Flattens a keyed list into a list of it's contents
-/proc/flatten_list(list/key_list)
- if(!islist(key_list))
- return null
- . = list()
- for(var/key in key_list)
- . |= key_list[key]
-
-/proc/make_associative(list/flat_list)
- . = list()
- for(var/thing in flat_list)
- .[thing] = TRUE
-
-//Picks from the list, with some safeties, and returns the "default" arg if it fails
-#define DEFAULTPICK(L, default) ((islist(L) && length(L)) ? pick(L) : default)
-
-/* Definining a counter as a series of key -> numeric value entries
-
- * All these procs modify in place.
-*/
-
-/proc/counterlist_scale(list/L, scalar)
- var/list/out = list()
- for(var/key in L)
- out[key] = L[key] * scalar
- . = out
-
-/proc/counterlist_sum(list/L)
- . = 0
- for(var/key in L)
- . += L[key]
-
-/proc/counterlist_normalise(list/L)
- var/avg = counterlist_sum(L)
- if(avg != 0)
- . = counterlist_scale(L, 1 / avg)
- else
- . = L
-
-/proc/counterlist_combine(list/L1, list/L2)
- for(var/key in L2)
- var/other_value = L2[key]
- if(key in L1)
- L1[key] += other_value
- else
- L1[key] = other_value
-
-/proc/assoc_list_strip_value(list/input)
- var/list/ret = list()
- for(var/key in input)
- ret += key
- return ret
-
-/proc/compare_list(list/l,list/d)
- if(!islist(l) || !islist(d))
- return FALSE
-
- if(l.len != d.len)
- return FALSE
-
- for(var/i in 1 to l.len)
- if(l[i] != d[i])
- return FALSE
-
- return TRUE
+/*
+ * Holds procs to help with list operations
+ * Contains groups:
+ * Misc
+ * Sorting
+ */
+
+/*
+ * Misc
+ */
+
+#define LAZYINITLIST(L) if (!L) L = list()
+#define UNSETEMPTY(L) if (L && !length(L)) L = null
+#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
+#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
+#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
+#define LAZYFIND(L, V) L ? L.Find(V) : 0
+#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
+#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
+#define LAZYLEN(L) length(L)
+#define LAZYCLEARLIST(L) if(L) L.Cut()
+#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
+#define reverseList(L) reverseRange(L.Copy())
+
+// binary search sorted insert
+// IN: Object to be inserted
+// LIST: List to insert object into
+// TYPECONT: The typepath of the contents of the list
+// COMPARE: The variable on the objects to compare
+#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \
+ var/__BIN_CTTL = length(LIST);\
+ if(!__BIN_CTTL) {\
+ LIST += IN;\
+ } else {\
+ var/__BIN_LEFT = 1;\
+ var/__BIN_RIGHT = __BIN_CTTL;\
+ var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
+ var/##TYPECONT/__BIN_ITEM;\
+ while(__BIN_LEFT < __BIN_RIGHT) {\
+ __BIN_ITEM = LIST[__BIN_MID];\
+ if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\
+ __BIN_LEFT = __BIN_MID + 1;\
+ } else {\
+ __BIN_RIGHT = __BIN_MID;\
+ };\
+ __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
+ };\
+ __BIN_ITEM = LIST[__BIN_MID];\
+ __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\
+ LIST.Insert(__BIN_MID, IN);\
+ }
+
+//Returns a list in plain english as a string
+/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
+ var/total = input.len
+ if (!total)
+ return "[nothing_text]"
+ else if (total == 1)
+ return "[input[1]]"
+ else if (total == 2)
+ return "[input[1]][and_text][input[2]]"
+ else
+ var/output = ""
+ var/index = 1
+ while (index < total)
+ if (index == total - 1)
+ comma_text = final_comma_text
+
+ output += "[input[index]][comma_text]"
+ index++
+
+ return "[output][and_text][input[index]]"
+
+//Returns list element or null. Should prevent "index out of bounds" error.
+/proc/listgetindex(list/L, index)
+ if(LAZYLEN(L))
+ if(isnum(index) && ISINTEGER(index))
+ if(ISINRANGE(index,1,L.len))
+ return L[index]
+ else if(index in L)
+ return L[index]
+ return
+
+//Return either pick(list) or null if list is not of type /list or is empty
+/proc/safepick(list/L)
+ if(LAZYLEN(L))
+ return pick(L)
+
+//Checks if the list is empty
+/proc/isemptylist(list/L)
+ if(!L.len)
+ return TRUE
+ return FALSE
+
+//Checks for specific types in a list
+/proc/is_type_in_list(atom/A, list/L)
+ if(!LAZYLEN(L) || !A)
+ return FALSE
+ for(var/type in L)
+ if(istype(A, type))
+ return TRUE
+ return FALSE
+
+//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
+#define is_type_in_typecache(A, L) (A && length(L) && L[(ispath(A) ? A : A:type)])
+
+//Checks for a string in a list
+/proc/is_string_in_list(string, list/L)
+ if(!LAZYLEN(L) || !string)
+ return
+ for(var/V in L)
+ if(string == V)
+ return TRUE
+ return
+
+//Removes a string from a list
+/proc/remove_strings_from_list(string, list/L)
+ if(!LAZYLEN(L) || !string)
+ return
+ for(var/V in L)
+ if(V == string)
+ L -= V //No return here so that it removes all strings of that type
+ return
+
+//returns a new list with only atoms that are in typecache L
+/proc/typecache_filter_list(list/atoms, list/typecache)
+ RETURN_TYPE(/list)
+ . = list()
+ for(var/thing in atoms)
+ var/atom/A = thing
+ if (typecache[A.type])
+ . += A
+
+/proc/typecache_filter_list_reverse(list/atoms, list/typecache)
+ RETURN_TYPE(/list)
+ . = list()
+ for(var/thing in atoms)
+ var/atom/A = thing
+ if(!typecache[A.type])
+ . += A
+
+/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude)
+ . = list()
+ for(var/thing in atoms)
+ var/atom/A = thing
+ if(typecache_include[A.type] && !typecache_exclude[A.type])
+ . += A
+
+//Like typesof() or subtypesof(), but returns a typecache instead of a list
+/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
+ if(ispath(path))
+ var/list/types = list()
+ if(only_root_path)
+ types = list(path)
+ else
+ types = ignore_root_path ? subtypesof(path) : typesof(path)
+ var/list/L = list()
+ for(var/T in types)
+ L[T] = TRUE
+ return L
+ else if(islist(path))
+ var/list/pathlist = path
+ var/list/L = list()
+ if(ignore_root_path)
+ for(var/P in pathlist)
+ for(var/T in subtypesof(P))
+ L[T] = TRUE
+ else
+ for(var/P in pathlist)
+ if(only_root_path)
+ L[P] = TRUE
+ else
+ for(var/T in typesof(P))
+ L[T] = TRUE
+ return L
+
+//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
+/proc/clearlist(list/list)
+ if(istype(list))
+ list.len = 0
+ return
+
+//Removes any null entries from the list
+//Returns TRUE if the list had nulls, FALSE otherwise
+/proc/listclearnulls(list/L)
+ var/start_len = L.len
+ var/list/N = new(start_len)
+ L -= N
+ return L.len < start_len
+
+/*
+ * Returns list containing all the entries from first list that are not present in second.
+ * If skiprep = 1, repeated elements are treated as one.
+ * If either of arguments is not a list, returns null
+ */
+/proc/difflist(list/first, list/second, skiprep=0)
+ if(!islist(first) || !islist(second))
+ return
+ var/list/result = new
+ if(skiprep)
+ for(var/e in first)
+ if(!(e in result) && !(e in second))
+ result += e
+ else
+ result = first - second
+ return result
+
+/*
+ * Returns list containing entries that are in either list but not both.
+ * If skipref = 1, repeated elements are treated as one.
+ * If either of arguments is not a list, returns null
+ */
+/proc/uniquemergelist(list/first, list/second, skiprep=0)
+ if(!islist(first) || !islist(second))
+ return
+ var/list/result = new
+ if(skiprep)
+ result = difflist(first, second, skiprep)+difflist(second, first, skiprep)
+ else
+ result = first ^ second
+ return result
+
+//Picks a random element from a list based on a weighting system:
+//1. Adds up the total of weights for each element
+//2. Gets a number between 1 and that total
+//3. For each element in the list, subtracts its weighting from that number
+//4. If that makes the number 0 or less, return that element.
+/proc/pickweight(list/L)
+ var/total = 0
+ var/item
+ for (item in L)
+ if (!L[item])
+ L[item] = 1
+ total += L[item]
+
+ total *= rand() // Yogs -- Allows for noninteger weights
+ for (item in L)
+ total -=L [item]
+ if (total <= 0)
+ return item
+
+ return null
+
+/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be.
+ var/total = 0
+ var/item
+ for (item in L)
+ if (!L[item])
+ L[item] = 0
+ total += L[item]
+
+ total = rand(0, total)
+ for (item in L)
+ total -=L [item]
+ if (total <= 0 && L[item])
+ return item
+
+ return null
+
+//Pick a random element from the list and remove it from the list.
+/proc/pick_n_take(list/L)
+ RETURN_TYPE(L[_].type)
+ if(L.len)
+ var/picked = rand(1,L.len)
+ . = L[picked]
+ L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
+
+//Returns the top(last) element from the list and removes it from the list (typical stack function)
+/proc/pop(list/L)
+ if(L.len)
+ . = L[L.len]
+ L.len--
+
+/proc/popleft(list/L)
+ if(L.len)
+ . = L[1]
+ L.Cut(1,2)
+
+/proc/sorted_insert(list/L, thing, comparator)
+ var/pos = L.len
+ while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
+ pos--
+ L.Insert(pos+1, thing)
+
+// Returns the next item in a list
+/proc/next_list_item(item, list/L)
+ var/i
+ i = L.Find(item)
+ if(i == L.len)
+ i = 1
+ else
+ i++
+ return L[i]
+
+// Returns the previous item in a list
+/proc/previous_list_item(item, list/L)
+ var/i
+ i = L.Find(item)
+ if(i == 1)
+ i = L.len
+ else
+ i--
+ return L[i]
+
+//Randomize: Return the list in a random order
+/proc/shuffle(list/L)
+ if(!L)
+ return
+ L = L.Copy()
+
+ for(var/i=1, i= 0 ? /proc/cmp_ckey_asc : /proc/cmp_ckey_dsc)
+
+//Specifically for record datums in a list.
+/proc/sortRecord(list/L, field = "name", order = 1)
+ GLOB.cmp_field = field
+ return sortTim(L, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc)
+
+//any value in a list
+/proc/sortList(list/L, cmp=/proc/cmp_text_asc)
+ return sortTim(L.Copy(), cmp)
+
+//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
+/proc/sortNames(list/L, order=1)
+ return sortTim(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
+
+
+//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
+/proc/bitfield2list(bitfield = 0, list/wordlist)
+ var/list/r = list()
+ if(islist(wordlist))
+ var/max = min(wordlist.len,16)
+ var/bit = 1
+ for(var/i=1, i<=max, i++)
+ if(bitfield & bit)
+ r += wordlist[i]
+ bit = bit << 1
+ else
+ for(var/bit=1, bit<=65535, bit = bit << 1)
+ if(bitfield & bit)
+ r += bit
+
+ return r
+
+// Returns the key based on the index
+#define KEYBYINDEX(L, index) (((index <= length(L)) && (index > 0)) ? L[index] : null)
+
+/proc/count_by_type(list/L, type)
+ var/i = 0
+ for(var/T in L)
+ if(istype(T, type))
+ i++
+ return i
+
+/proc/find_record(field, value, list/L)
+ for(var/datum/data/record/R in L)
+ if(R.fields[field] == value)
+ return R
+
+
+//Move a single element from position fromIndex within a list, to position toIndex
+//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
+//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
+//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
+//fromIndex and toIndex must be in the range [1,L.len+1]
+//This will preserve associations ~Carnie
+/proc/moveElement(list/L, fromIndex, toIndex)
+ if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
+ return
+ if(fromIndex > toIndex)
+ ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
+
+ L.Insert(toIndex, null)
+ L.Swap(fromIndex, toIndex)
+ L.Cut(fromIndex, fromIndex+1)
+
+
+//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
+//Same as moveElement but for ranges of elements
+//This will preserve associations ~Carnie
+/proc/moveRange(list/L, fromIndex, toIndex, len=1)
+ var/distance = abs(toIndex - fromIndex)
+ if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
+ if(fromIndex <= toIndex)
+ return //no need to move
+ fromIndex += len //we want to shift left instead of right
+
+ for(var/i=0, i toIndex)
+ fromIndex += len
+
+ for(var/i=0, i distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
+ if(fromIndex < toIndex)
+ toIndex += len
+ else
+ fromIndex += len
+
+ for(var/i=0, i fromIndex)
+ var/a = toIndex
+ toIndex = fromIndex
+ fromIndex = a
+
+ for(var/i=0, i 512
+#error Remie said that lummox was adding a way to get a lists
+#error contents via list.values, if that is true remove this
+#error otherwise, update the version and bug lummox
+#endif
+//Flattens a keyed list into a list of it's contents
+/proc/flatten_list(list/key_list)
+ if(!islist(key_list))
+ return null
+ . = list()
+ for(var/key in key_list)
+ . |= key_list[key]
+
+/proc/make_associative(list/flat_list)
+ . = list()
+ for(var/thing in flat_list)
+ .[thing] = TRUE
+
+//Picks from the list, with some safeties, and returns the "default" arg if it fails
+#define DEFAULTPICK(L, default) ((islist(L) && length(L)) ? pick(L) : default)
+
+/* Definining a counter as a series of key -> numeric value entries
+
+ * All these procs modify in place.
+*/
+
+/proc/counterlist_scale(list/L, scalar)
+ var/list/out = list()
+ for(var/key in L)
+ out[key] = L[key] * scalar
+ . = out
+
+/proc/counterlist_sum(list/L)
+ . = 0
+ for(var/key in L)
+ . += L[key]
+
+/proc/counterlist_normalise(list/L)
+ var/avg = counterlist_sum(L)
+ if(avg != 0)
+ . = counterlist_scale(L, 1 / avg)
+ else
+ . = L
+
+/proc/counterlist_combine(list/L1, list/L2)
+ for(var/key in L2)
+ var/other_value = L2[key]
+ if(key in L1)
+ L1[key] += other_value
+ else
+ L1[key] = other_value
+
+/proc/assoc_list_strip_value(list/input)
+ var/list/ret = list()
+ for(var/key in input)
+ ret += key
+ return ret
+
+/proc/compare_list(list/l,list/d)
+ if(!islist(l) || !islist(d))
+ return FALSE
+
+ if(l.len != d.len)
+ return FALSE
+
+ for(var/i in 1 to l.len)
+ if(l[i] != d[i])
+ return FALSE
+
+ return TRUE
diff --git a/code/__HELPERS/_string_lists.dm b/code/__HELPERS/_string_lists.dm
index f2f57bb15db6..766ea9e5c22a 100644
--- a/code/__HELPERS/_string_lists.dm
+++ b/code/__HELPERS/_string_lists.dm
@@ -1,41 +1,41 @@
-#define pick_list(FILE, KEY) (pick(strings(FILE, KEY)))
-#define pick_list_replacements(FILE, KEY) (strings_replacement(FILE, KEY))
-#define json_load(FILE) (json_decode(file2text(FILE)))
-
-GLOBAL_LIST(string_cache)
-GLOBAL_VAR(string_filename_current_key)
-
-
-/proc/strings_replacement(filename, key, directory = "strings")
- load_strings_file(filename, directory)
-
- if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename]))
- var/response = pick(GLOB.string_cache[filename][key])
- var/regex/r = regex("@pick\\((\\D+?)\\)", "g")
- response = r.Replace(response, /proc/strings_subkey_lookup)
- return response
- else
- CRASH("strings list not found: [directory]/[filename], index=[key]")
-
-/proc/strings(filename as text, key as text, directory = "strings")
- load_strings_file(filename, directory)
- if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename]))
- return GLOB.string_cache[filename][key]
- else
- CRASH("strings list not found: [directory]/[filename], index=[key]")
-
-/proc/strings_subkey_lookup(match, group1)
- return pick_list(GLOB.string_filename_current_key, group1)
-
-/proc/load_strings_file(filename, directory = "strings")
- GLOB.string_filename_current_key = filename
- if(filename in GLOB.string_cache)
- return //no work to do
-
- if(!GLOB.string_cache)
- GLOB.string_cache = new
-
- if(fexists("[directory]/[filename]"))
- GLOB.string_cache[filename] = json_load("[directory]/[filename]")
- else
- CRASH("file not found: [directory]/[filename]")
+#define pick_list(FILE, KEY) (pick(strings(FILE, KEY)))
+#define pick_list_replacements(FILE, KEY) (strings_replacement(FILE, KEY))
+#define json_load(FILE) (json_decode(file2text(FILE)))
+
+GLOBAL_LIST(string_cache)
+GLOBAL_VAR(string_filename_current_key)
+
+
+/proc/strings_replacement(filename, key, directory = "strings")
+ load_strings_file(filename, directory)
+
+ if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename]))
+ var/response = pick(GLOB.string_cache[filename][key])
+ var/regex/r = regex("@pick\\((\\D+?)\\)", "g")
+ response = r.Replace(response, /proc/strings_subkey_lookup)
+ return response
+ else
+ CRASH("strings list not found: [directory]/[filename], index=[key]")
+
+/proc/strings(filename as text, key as text, directory = "strings")
+ load_strings_file(filename, directory)
+ if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename]))
+ return GLOB.string_cache[filename][key]
+ else
+ CRASH("strings list not found: [directory]/[filename], index=[key]")
+
+/proc/strings_subkey_lookup(match, group1)
+ return pick_list(GLOB.string_filename_current_key, group1)
+
+/proc/load_strings_file(filename, directory = "strings")
+ GLOB.string_filename_current_key = filename
+ if(filename in GLOB.string_cache)
+ return //no work to do
+
+ if(!GLOB.string_cache)
+ GLOB.string_cache = new
+
+ if(fexists("[directory]/[filename]"))
+ GLOB.string_cache[filename] = json_load("[directory]/[filename]")
+ else
+ CRASH("file not found: [directory]/[filename]")
diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm
index 5d6106cf9181..384c96ee7210 100644
--- a/code/__HELPERS/files.dm
+++ b/code/__HELPERS/files.dm
@@ -1,73 +1,73 @@
-//Sends resource files to client cache
-/client/proc/getFiles(...)
- for(var/file in args)
- src << browse_rsc(file)
-
-/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html"))
- var/path = root
-
- for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).")
- return
-
- return path
-
-#define FTPDELAY 200 //200 tick delay to discourage spam
-#define ADMIN_FTPDELAY_MODIFIER 0.5 //Admins get to spam files faster since we ~trust~ them!
-/* This proc is a failsafe to prevent spamming of file requests.
- It is just a timer that only permits a download every [FTPDELAY] ticks.
- This can be changed by modifying FTPDELAY's value above.
-
- PLEASE USE RESPONSIBLY, Some log files can reach sizes of 4MB! */
-/client/proc/file_spam_check()
- var/time_to_wait = GLOB.fileaccess_timer - world.time
- if(time_to_wait > 0)
- to_chat(src, "Error: file_spam_check(): Spam. Please wait [DisplayTimeText(time_to_wait)].")
- return 1
- var/delay = FTPDELAY
- if(holder)
- delay *= ADMIN_FTPDELAY_MODIFIER
- GLOB.fileaccess_timer = world.time + delay
- return 0
-#undef FTPDELAY
-#undef ADMIN_FTPDELAY_MODIFIER
-
-/proc/pathwalk(path)
- var/list/jobs = list(path)
- var/list/filenames = list()
-
- while(jobs.len)
- var/current_dir = pop(jobs)
- var/list/new_filenames = flist(current_dir)
- for(var/new_filename in new_filenames)
- // if filename ends in / it is a directory, append to currdir
- if(findtext(new_filename, "/", -1))
- jobs += current_dir + new_filename
- else
- filenames += current_dir + new_filename
- return filenames
-
-/proc/pathflatten(path)
- return replacetext(path, "/", "_")
+//Sends resource files to client cache
+/client/proc/getFiles(...)
+ for(var/file in args)
+ src << browse_rsc(file)
+
+/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html"))
+ var/path = root
+
+ for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).")
+ return
+
+ return path
+
+#define FTPDELAY 200 //200 tick delay to discourage spam
+#define ADMIN_FTPDELAY_MODIFIER 0.5 //Admins get to spam files faster since we ~trust~ them!
+/* This proc is a failsafe to prevent spamming of file requests.
+ It is just a timer that only permits a download every [FTPDELAY] ticks.
+ This can be changed by modifying FTPDELAY's value above.
+
+ PLEASE USE RESPONSIBLY, Some log files can reach sizes of 4MB! */
+/client/proc/file_spam_check()
+ var/time_to_wait = GLOB.fileaccess_timer - world.time
+ if(time_to_wait > 0)
+ to_chat(src, "Error: file_spam_check(): Spam. Please wait [DisplayTimeText(time_to_wait)].")
+ return 1
+ var/delay = FTPDELAY
+ if(holder)
+ delay *= ADMIN_FTPDELAY_MODIFIER
+ GLOB.fileaccess_timer = world.time + delay
+ return 0
+#undef FTPDELAY
+#undef ADMIN_FTPDELAY_MODIFIER
+
+/proc/pathwalk(path)
+ var/list/jobs = list(path)
+ var/list/filenames = list()
+
+ while(jobs.len)
+ var/current_dir = pop(jobs)
+ var/list/new_filenames = flist(current_dir)
+ for(var/new_filename in new_filenames)
+ // if filename ends in / it is a directory, append to currdir
+ if(findtext(new_filename, "/", -1))
+ jobs += current_dir + new_filename
+ else
+ filenames += current_dir + new_filename
+ return filenames
+
+/proc/pathflatten(path)
+ return replacetext(path, "/", "_")
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 6ea305475a09..6161753e49aa 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -1,573 +1,573 @@
-//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
-#define RANGE_TURFS(RADIUS, CENTER) \
- block( \
- locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
- locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
- )
-
-#define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL))
-#define CULT_POLL_WAIT 2400
-
-/proc/get_area_name(atom/X, format_text = FALSE)
- var/area/A = isarea(X) ? X : get_area(X)
- if(!A)
- return null
- return format_text ? format_text(A.name) : A.name
-
-/proc/get_areas_in_range(dist=0, atom/center=usr)
- if(!dist)
- var/turf/T = get_turf(center)
- return T ? list(T.loc) : list()
- if(!center)
- return list()
-
- var/list/turfs = RANGE_TURFS(dist, center)
- var/list/areas = list()
- for(var/V in turfs)
- var/turf/T = V
- areas |= T.loc
- return areas
-
-/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(.)
-
-/proc/get_open_turf_in_dir(atom/center, dir)
- var/turf/open/T = get_ranged_target_turf(center, dir, 1)
- if(istype(T))
- return T
-
-/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(.)
-
-/proc/get_adjacent_open_areas(atom/center)
- . = list()
- var/list/adjacent_turfs = get_adjacent_open_turfs(center)
- for(var/I in adjacent_turfs)
- . |= get_area(I)
-
-// Like view but bypasses luminosity check
-
-/proc/get_hear(range, atom/source)
-
- var/lum = source.luminosity
- source.luminosity = 6
-
- var/list/heard = view(range, source)
- source.luminosity = lum
-
- return heard
-
-/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/C in GLOB.alive_mob_list)
- if(!istype(C, check_type))
- continue
- if(C == must_be_alone)
- continue
- if(our_area == get_area(C))
- return 0
- return 1
-
-//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster.
-//And lo and behold, it is, and it's more accurate to boot.
-/proc/cheap_hypotenuse(Ax,Ay,Bx,By)
- return sqrt(abs(Ax - Bx)**2 + abs(Ay - By)**2) //A squared + B squared = C squared
-
-/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)
- 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
-
-
-//This is the new version of recursive_mob_check, used for say().
-//The other proc was left intact because morgue trays use it.
-//Sped this up again for real this time
-/proc/recursive_hear_check(O)
- var/list/processing_list = list(O)
- . = list()
- while(processing_list.len)
- var/atom/A = processing_list[1]
- if(A.flags_1 & HEAR_1)
- . += A
- processing_list.Cut(1, 2)
- processing_list += A.contents
-
-/** recursive_organ_check
- * inputs: O (object to start with)
- * outputs:
- * description: A pseudo-recursive loop based off of the recursive mob check, this check looks for any organs held
- * within 'O', toggling their frozen flag. This check excludes items held within other safe organ
- * storage units, so that only the lowest level of container dictates whether we do or don't decompose
- */
-/proc/recursive_organ_check(atom/O)
-
- var/list/processing_list = list(O)
- var/list/processed_list = list()
- var/index = 1
- var/obj/item/organ/found_organ
-
- while(index <= length(processing_list))
-
- var/atom/A = processing_list[index]
-
- if(istype(A, /obj/item/organ))
- found_organ = A
- found_organ.organ_flags ^= ORGAN_FROZEN
-
- else if(istype(A, /mob/living/carbon))
- var/mob/living/carbon/Q = A
- for(var/organ in Q.internal_organs)
- found_organ = organ
- found_organ.organ_flags ^= ORGAN_FROZEN
-
- for(var/atom/B in A) //objects held within other objects are added to the processing list, unless that object is something that can hold organs safely
- if(!processed_list[B] && !istype(B, /obj/structure/closet/crate/freezer) && !istype(B, /obj/structure/closet/secure_closet/freezer))
- processing_list+= B
-
- index++
- processed_list[A] = A
-
- return
-
-// Better recursive loop, technically sort of not actually recursive cause that shit is retarded, enjoy.
-//No need for a recursive limit either
-/proc/recursive_mob_check(atom/O,client_check=1,sight_check=1,include_radio=1)
-
- var/list/processing_list = list(O)
- var/list/processed_list = list()
- var/list/found_mobs = list()
-
- while(processing_list.len)
-
- var/atom/A = processing_list[1]
- var/passed = 0
-
- if(ismob(A))
- var/mob/A_tmp = A
- passed=1
-
- if(client_check && !A_tmp.client)
- passed=0
-
- if(sight_check && !isInSight(A_tmp, O))
- passed=0
-
- else if(include_radio && istype(A, /obj/item/radio))
- passed=1
-
- if(sight_check && !isInSight(A, O))
- passed=0
-
- if(passed)
- found_mobs |= A
-
- for(var/atom/B in A)
- if(!processed_list[B])
- processing_list |= B
-
- processing_list.Cut(1, 2)
- processed_list[A] = A
-
- return found_mobs
-
-
-/proc/get_hearers_in_view(R, atom/source)
- // Returns a list of hearers in view(R) from source (ignoring luminosity). Used in saycode.
- var/turf/T = get_turf(source)
- . = list()
-
- if(!T)
- return
-
- var/list/processing_list = list()
- if (R == 0) // if the range is zero, we know exactly where to look for, we can skip view
- processing_list += T.contents // We can shave off one iteration by assuming turfs cannot hear
- else // A variation of get_hear inlined here to take advantage of the compiler's fastpath for obj/mob in view
- var/lum = T.luminosity
- T.luminosity = 6 // This is the maximum luminosity
- for(var/mob/M in view(R, T))
- processing_list += M
- for(var/obj/O in view(R, T))
- processing_list += O
- T.luminosity = lum
-
- while(processing_list.len) // recursive_hear_check inlined here
- var/atom/A = processing_list[1]
- if(A.flags_1 & HEAR_1)
- . += A
- processing_list.Cut(1, 2)
- processing_list += A.contents
-
-/proc/get_mobs_in_radio_ranges(list/obj/item/radio/radios)
- . = list()
- // Returns a list of mobs who can hear any of the radios given in @radios
- for(var/obj/item/radio/R in radios)
- if(R)
- . |= get_hearers_in_view(R.canhear_range, R)
-
-
-#define SIGNV(X) ((X<0)?-1:1)
-
-/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 abs (dx)) //slope is above 1:1 (move horizontally in a tie)
- if(dy > 0)
- return get_step(start, SOUTH)
- else
- return get_step(start, NORTH)
- else
- if(dx > 0)
- return get_step(start, WEST)
- else
- return get_step(start, EAST)
-
-
-/proc/try_move_adjacent(atom/movable/AM)
- var/turf/T = get_turf(AM)
- for(var/direction in GLOB.cardinals)
- if(AM.Move(get_step(T, direction)))
- break
-
-/proc/get_mob_by_key(key)
- var/ckey = ckey(key)
- for(var/i in GLOB.player_list)
- var/mob/M = i
- if(M.ckey == ckey)
- return M
- return null
-
-/proc/considered_alive(datum/mind/M, enforce_human = TRUE)
- if(M && M.current)
- if(enforce_human)
- var/mob/living/carbon/human/H
- if(ishuman(M.current))
- H = M.current
- return M.current.stat != DEAD && !issilicon(M.current) && !isbrain(M.current) && (!H || H.dna.species.id != "memezombies")
- else if(isliving(M.current))
- return M.current.stat != DEAD
- return FALSE
-
-/proc/considered_afk(datum/mind/M)
- return !M || !M.current || !M.current.client || M.current.client.is_afk()
-
-/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
- if(!isobj(O))
- O = new /obj/screen/text()
- O.maptext = maptext
- O.maptext_height = maptext_height
- O.maptext_width = maptext_width
- O.screen_loc = screen_loc
- return O
-
-/proc/remove_images_from_clients(image/I, list/show_to)
- for(var/client/C in show_to)
- C.images -= I
-
-/proc/flick_overlay(image/I, list/show_to, duration)
- for(var/client/C in show_to)
- C.images += I
- addtimer(CALLBACK(GLOBAL_PROC, /proc/remove_images_from_clients, I, show_to), duration, TIMER_CLIENT_TIME)
-
-/proc/flick_overlay_view(image/I, atom/target, duration) //wrapper for the above, flicks to everyone who can see the target atom
- var/list/viewing = list()
- for(var/m in viewers(target))
- var/mob/M = m
- if(M.client)
- viewing += M.client
- flick_overlay(I, viewing, duration)
-
-/proc/get_active_player_count(var/alive_check = 0, var/afk_check = 0, var/human_check = 0)
- // Get active players who are playing in the round
- var/active_players = 0
- for(var/i = 1; i <= GLOB.player_list.len; i++)
- var/mob/M = GLOB.player_list[i]
- if(M && M.client)
- if(alive_check && M.stat)
- continue
- else if(afk_check && M.client.is_afk())
- continue
- else if(human_check && !ishuman(M))
- continue
- else if(isnewplayer(M)) // exclude people in the lobby
- continue
- else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
- var/mob/dead/observer/O = M
- if(O.started_as_observer) // Exclude people who started as observers
- continue
- active_players++
- return active_players
-
-/proc/showCandidatePollWindow(mob/M, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE)
- set waitfor = 0
-
- SEND_SOUND(M, 'sound/misc/notice2.ogg') //Alerting them to their consideration
- if(flashwindow)
- window_flash(M.client)
- switch(ignore_category ? askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No", StealFocus=0, Timeout=poll_time))
- if(1)
- to_chat(M, "Choice registered: Yes.")
- if(time_passed + poll_time <= world.time)
- to_chat(M, "Sorry, you answered too late to be considered!")
- SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
- candidates -= M
- else
- candidates += M
- if(2)
- to_chat(M, "Choice registered: No.")
- candidates -= M
- if(3)
- var/list/L = GLOB.poll_ignore[ignore_category]
- if(!L)
- GLOB.poll_ignore[ignore_category] = list()
- GLOB.poll_ignore[ignore_category] += M.ckey
- to_chat(M, "Choice registered: Never for this round.")
- candidates -= M
- else
- candidates -= M
-
-/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE)
- var/list/candidates = list()
-
- for(var/mob/dead/observer/G in GLOB.player_list)
- candidates += G
-
- return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
-
-/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null)
- var/time_passed = world.time
- if (!Question)
- Question = "Would you like to be a special role?"
- var/list/result = list()
- for(var/m in group)
- var/mob/M = m
- if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && M.ckey in GLOB.poll_ignore[ignore_category]))
- continue
- if(be_special_flag)
- if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special))
- continue
- if(gametypeCheck)
- if(!gametypeCheck.age_check(M.client))
- continue
- if(jobbanType)
- if(is_banned_from(M.ckey, list(jobbanType, ROLE_SYNDICATE)) || QDELETED(M))
- continue
-
- showCandidatePollWindow(M, poll_time, Question, result, ignore_category, time_passed, flashwindow)
- sleep(poll_time)
-
- //Check all our candidates, to make sure they didn't log off or get deleted during the wait period.
- for(var/mob/M in result)
- if(!M.key || !M.client)
- result -= M
-
- listclearnulls(result)
-
- return result
-
-/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null)
- var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
- if(!M || QDELETED(M) || !M.loc)
- return list()
- return L
-
-/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null)
- var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
- var/i=1
- for(var/v in mobs)
- var/atom/A = v
- if(!A || QDELETED(A) || !A.loc)
- mobs.Cut(i,i+1)
- else
- ++i
- return L
-
-/proc/poll_helper(var/mob/living/M)
-
-/proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character
- if(!G_found || !G_found.key)
- return
-
- //First we spawn a dude.
- var/mob/living/carbon/human/new_character = new//The mob being spawned.
- SSjob.SendToLateJoin(new_character)
-
- G_found.client.prefs.copy_to(new_character)
- new_character.dna.update_dna_identity()
- new_character.key = G_found.key
-
- return new_character
-
-/proc/send_to_playing_players(thing) //sends a whatever to all playing players; use instead of to_chat(world, where needed)
- for(var/M in GLOB.player_list)
- if(M && !isnewplayer(M))
- to_chat(M, thing)
-
-/proc/window_flash(client/C, ignorepref = FALSE)
- if(ismob(C))
- var/mob/M = C
- if(M.client)
- C = M.client
- if(!C || (!C.prefs.windowflashing && !ignorepref))
- return
- winset(C, "mainwindow", "flash=5")
-
-//Recursively checks if an item is inside a given type, even through layers of storage. Returns the atom if it finds it.
-/proc/recursive_loc_check(atom/movable/target, type)
- var/atom/A = target
- if(istype(A, type))
- return A
-
- while(!istype(A.loc, type))
- if(!A.loc)
- return
- A = A.loc
-
- return A.loc
-
-/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank)
- if(!SSticker.IsRoundInProgress() || QDELETED(character))
- return
- var/area/A = get_area(character)
- deadchat_broadcast(" has arrived at the station at [A.name].", "[character.real_name] ([rank])", follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE)
- if((!GLOB.announcement_systems.len) || (!character.mind))
- return
- if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role))
- return
-
- var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
- announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common
-
-/proc/GetRedPart(const/hexa)
- return hex2num(copytext(hexa, 2, 4))
-
-/proc/GetGreenPart(const/hexa)
- return hex2num(copytext(hexa, 4, 6))
-
-/proc/GetBluePart(const/hexa)
- return hex2num(copytext(hexa, 6, 8))
-
-/proc/lavaland_equipment_pressure_check(turf/T)
- . = FALSE
- if(!istype(T))
- return
- var/datum/gas_mixture/environment = T.return_air()
- if(!istype(environment))
- return
- var/pressure = environment.return_pressure()
- if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
- . = TRUE
+//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
+#define RANGE_TURFS(RADIUS, CENTER) \
+ block( \
+ locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
+ locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
+ )
+
+#define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL))
+#define CULT_POLL_WAIT 2400
+
+/proc/get_area_name(atom/X, format_text = FALSE)
+ var/area/A = isarea(X) ? X : get_area(X)
+ if(!A)
+ return null
+ return format_text ? format_text(A.name) : A.name
+
+/proc/get_areas_in_range(dist=0, atom/center=usr)
+ if(!dist)
+ var/turf/T = get_turf(center)
+ return T ? list(T.loc) : list()
+ if(!center)
+ return list()
+
+ var/list/turfs = RANGE_TURFS(dist, center)
+ var/list/areas = list()
+ for(var/V in turfs)
+ var/turf/T = V
+ areas |= T.loc
+ return areas
+
+/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(.)
+
+/proc/get_open_turf_in_dir(atom/center, dir)
+ var/turf/open/T = get_ranged_target_turf(center, dir, 1)
+ if(istype(T))
+ return T
+
+/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(.)
+
+/proc/get_adjacent_open_areas(atom/center)
+ . = list()
+ var/list/adjacent_turfs = get_adjacent_open_turfs(center)
+ for(var/I in adjacent_turfs)
+ . |= get_area(I)
+
+// Like view but bypasses luminosity check
+
+/proc/get_hear(range, atom/source)
+
+ var/lum = source.luminosity
+ source.luminosity = 6
+
+ var/list/heard = view(range, source)
+ source.luminosity = lum
+
+ return heard
+
+/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/C in GLOB.alive_mob_list)
+ if(!istype(C, check_type))
+ continue
+ if(C == must_be_alone)
+ continue
+ if(our_area == get_area(C))
+ return 0
+ return 1
+
+//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster.
+//And lo and behold, it is, and it's more accurate to boot.
+/proc/cheap_hypotenuse(Ax,Ay,Bx,By)
+ return sqrt(abs(Ax - Bx)**2 + abs(Ay - By)**2) //A squared + B squared = C squared
+
+/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)
+ 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
+
+
+//This is the new version of recursive_mob_check, used for say().
+//The other proc was left intact because morgue trays use it.
+//Sped this up again for real this time
+/proc/recursive_hear_check(O)
+ var/list/processing_list = list(O)
+ . = list()
+ while(processing_list.len)
+ var/atom/A = processing_list[1]
+ if(A.flags_1 & HEAR_1)
+ . += A
+ processing_list.Cut(1, 2)
+ processing_list += A.contents
+
+/** recursive_organ_check
+ * inputs: O (object to start with)
+ * outputs:
+ * description: A pseudo-recursive loop based off of the recursive mob check, this check looks for any organs held
+ * within 'O', toggling their frozen flag. This check excludes items held within other safe organ
+ * storage units, so that only the lowest level of container dictates whether we do or don't decompose
+ */
+/proc/recursive_organ_check(atom/O)
+
+ var/list/processing_list = list(O)
+ var/list/processed_list = list()
+ var/index = 1
+ var/obj/item/organ/found_organ
+
+ while(index <= length(processing_list))
+
+ var/atom/A = processing_list[index]
+
+ if(istype(A, /obj/item/organ))
+ found_organ = A
+ found_organ.organ_flags ^= ORGAN_FROZEN
+
+ else if(istype(A, /mob/living/carbon))
+ var/mob/living/carbon/Q = A
+ for(var/organ in Q.internal_organs)
+ found_organ = organ
+ found_organ.organ_flags ^= ORGAN_FROZEN
+
+ for(var/atom/B in A) //objects held within other objects are added to the processing list, unless that object is something that can hold organs safely
+ if(!processed_list[B] && !istype(B, /obj/structure/closet/crate/freezer) && !istype(B, /obj/structure/closet/secure_closet/freezer))
+ processing_list+= B
+
+ index++
+ processed_list[A] = A
+
+ return
+
+// Better recursive loop, technically sort of not actually recursive cause that shit is retarded, enjoy.
+//No need for a recursive limit either
+/proc/recursive_mob_check(atom/O,client_check=1,sight_check=1,include_radio=1)
+
+ var/list/processing_list = list(O)
+ var/list/processed_list = list()
+ var/list/found_mobs = list()
+
+ while(processing_list.len)
+
+ var/atom/A = processing_list[1]
+ var/passed = 0
+
+ if(ismob(A))
+ var/mob/A_tmp = A
+ passed=1
+
+ if(client_check && !A_tmp.client)
+ passed=0
+
+ if(sight_check && !isInSight(A_tmp, O))
+ passed=0
+
+ else if(include_radio && istype(A, /obj/item/radio))
+ passed=1
+
+ if(sight_check && !isInSight(A, O))
+ passed=0
+
+ if(passed)
+ found_mobs |= A
+
+ for(var/atom/B in A)
+ if(!processed_list[B])
+ processing_list |= B
+
+ processing_list.Cut(1, 2)
+ processed_list[A] = A
+
+ return found_mobs
+
+
+/proc/get_hearers_in_view(R, atom/source)
+ // Returns a list of hearers in view(R) from source (ignoring luminosity). Used in saycode.
+ var/turf/T = get_turf(source)
+ . = list()
+
+ if(!T)
+ return
+
+ var/list/processing_list = list()
+ if (R == 0) // if the range is zero, we know exactly where to look for, we can skip view
+ processing_list += T.contents // We can shave off one iteration by assuming turfs cannot hear
+ else // A variation of get_hear inlined here to take advantage of the compiler's fastpath for obj/mob in view
+ var/lum = T.luminosity
+ T.luminosity = 6 // This is the maximum luminosity
+ for(var/mob/M in view(R, T))
+ processing_list += M
+ for(var/obj/O in view(R, T))
+ processing_list += O
+ T.luminosity = lum
+
+ while(processing_list.len) // recursive_hear_check inlined here
+ var/atom/A = processing_list[1]
+ if(A.flags_1 & HEAR_1)
+ . += A
+ processing_list.Cut(1, 2)
+ processing_list += A.contents
+
+/proc/get_mobs_in_radio_ranges(list/obj/item/radio/radios)
+ . = list()
+ // Returns a list of mobs who can hear any of the radios given in @radios
+ for(var/obj/item/radio/R in radios)
+ if(R)
+ . |= get_hearers_in_view(R.canhear_range, R)
+
+
+#define SIGNV(X) ((X<0)?-1:1)
+
+/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 abs (dx)) //slope is above 1:1 (move horizontally in a tie)
+ if(dy > 0)
+ return get_step(start, SOUTH)
+ else
+ return get_step(start, NORTH)
+ else
+ if(dx > 0)
+ return get_step(start, WEST)
+ else
+ return get_step(start, EAST)
+
+
+/proc/try_move_adjacent(atom/movable/AM)
+ var/turf/T = get_turf(AM)
+ for(var/direction in GLOB.cardinals)
+ if(AM.Move(get_step(T, direction)))
+ break
+
+/proc/get_mob_by_key(key)
+ var/ckey = ckey(key)
+ for(var/i in GLOB.player_list)
+ var/mob/M = i
+ if(M.ckey == ckey)
+ return M
+ return null
+
+/proc/considered_alive(datum/mind/M, enforce_human = TRUE)
+ if(M && M.current)
+ if(enforce_human)
+ var/mob/living/carbon/human/H
+ if(ishuman(M.current))
+ H = M.current
+ return M.current.stat != DEAD && !issilicon(M.current) && !isbrain(M.current) && (!H || H.dna.species.id != "memezombies")
+ else if(isliving(M.current))
+ return M.current.stat != DEAD
+ return FALSE
+
+/proc/considered_afk(datum/mind/M)
+ return !M || !M.current || !M.current.client || M.current.client.is_afk()
+
+/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
+ if(!isobj(O))
+ O = new /obj/screen/text()
+ O.maptext = maptext
+ O.maptext_height = maptext_height
+ O.maptext_width = maptext_width
+ O.screen_loc = screen_loc
+ return O
+
+/proc/remove_images_from_clients(image/I, list/show_to)
+ for(var/client/C in show_to)
+ C.images -= I
+
+/proc/flick_overlay(image/I, list/show_to, duration)
+ for(var/client/C in show_to)
+ C.images += I
+ addtimer(CALLBACK(GLOBAL_PROC, /proc/remove_images_from_clients, I, show_to), duration, TIMER_CLIENT_TIME)
+
+/proc/flick_overlay_view(image/I, atom/target, duration) //wrapper for the above, flicks to everyone who can see the target atom
+ var/list/viewing = list()
+ for(var/m in viewers(target))
+ var/mob/M = m
+ if(M.client)
+ viewing += M.client
+ flick_overlay(I, viewing, duration)
+
+/proc/get_active_player_count(var/alive_check = 0, var/afk_check = 0, var/human_check = 0)
+ // Get active players who are playing in the round
+ var/active_players = 0
+ for(var/i = 1; i <= GLOB.player_list.len; i++)
+ var/mob/M = GLOB.player_list[i]
+ if(M && M.client)
+ if(alive_check && M.stat)
+ continue
+ else if(afk_check && M.client.is_afk())
+ continue
+ else if(human_check && !ishuman(M))
+ continue
+ else if(isnewplayer(M)) // exclude people in the lobby
+ continue
+ else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
+ var/mob/dead/observer/O = M
+ if(O.started_as_observer) // Exclude people who started as observers
+ continue
+ active_players++
+ return active_players
+
+/proc/showCandidatePollWindow(mob/M, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE)
+ set waitfor = 0
+
+ SEND_SOUND(M, 'sound/misc/notice2.ogg') //Alerting them to their consideration
+ if(flashwindow)
+ window_flash(M.client)
+ switch(ignore_category ? askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [DisplayTimeText(poll_time)]!","Yes","No", StealFocus=0, Timeout=poll_time))
+ if(1)
+ to_chat(M, "Choice registered: Yes.")
+ if(time_passed + poll_time <= world.time)
+ to_chat(M, "Sorry, you answered too late to be considered!")
+ SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
+ candidates -= M
+ else
+ candidates += M
+ if(2)
+ to_chat(M, "Choice registered: No.")
+ candidates -= M
+ if(3)
+ var/list/L = GLOB.poll_ignore[ignore_category]
+ if(!L)
+ GLOB.poll_ignore[ignore_category] = list()
+ GLOB.poll_ignore[ignore_category] += M.ckey
+ to_chat(M, "Choice registered: Never for this round.")
+ candidates -= M
+ else
+ candidates -= M
+
+/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE)
+ var/list/candidates = list()
+
+ for(var/mob/dead/observer/G in GLOB.player_list)
+ candidates += G
+
+ return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
+
+/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null)
+ var/time_passed = world.time
+ if (!Question)
+ Question = "Would you like to be a special role?"
+ var/list/result = list()
+ for(var/m in group)
+ var/mob/M = m
+ if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && M.ckey in GLOB.poll_ignore[ignore_category]))
+ continue
+ if(be_special_flag)
+ if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special))
+ continue
+ if(gametypeCheck)
+ if(!gametypeCheck.age_check(M.client))
+ continue
+ if(jobbanType)
+ if(is_banned_from(M.ckey, list(jobbanType, ROLE_SYNDICATE)) || QDELETED(M))
+ continue
+
+ showCandidatePollWindow(M, poll_time, Question, result, ignore_category, time_passed, flashwindow)
+ sleep(poll_time)
+
+ //Check all our candidates, to make sure they didn't log off or get deleted during the wait period.
+ for(var/mob/M in result)
+ if(!M.key || !M.client)
+ result -= M
+
+ listclearnulls(result)
+
+ return result
+
+/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null)
+ var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
+ if(!M || QDELETED(M) || !M.loc)
+ return list()
+ return L
+
+/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null)
+ var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category)
+ var/i=1
+ for(var/v in mobs)
+ var/atom/A = v
+ if(!A || QDELETED(A) || !A.loc)
+ mobs.Cut(i,i+1)
+ else
+ ++i
+ return L
+
+/proc/poll_helper(var/mob/living/M)
+
+/proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character
+ if(!G_found || !G_found.key)
+ return
+
+ //First we spawn a dude.
+ var/mob/living/carbon/human/new_character = new//The mob being spawned.
+ SSjob.SendToLateJoin(new_character)
+
+ G_found.client.prefs.copy_to(new_character)
+ new_character.dna.update_dna_identity()
+ new_character.key = G_found.key
+
+ return new_character
+
+/proc/send_to_playing_players(thing) //sends a whatever to all playing players; use instead of to_chat(world, where needed)
+ for(var/M in GLOB.player_list)
+ if(M && !isnewplayer(M))
+ to_chat(M, thing)
+
+/proc/window_flash(client/C, ignorepref = FALSE)
+ if(ismob(C))
+ var/mob/M = C
+ if(M.client)
+ C = M.client
+ if(!C || (!C.prefs.windowflashing && !ignorepref))
+ return
+ winset(C, "mainwindow", "flash=5")
+
+//Recursively checks if an item is inside a given type, even through layers of storage. Returns the atom if it finds it.
+/proc/recursive_loc_check(atom/movable/target, type)
+ var/atom/A = target
+ if(istype(A, type))
+ return A
+
+ while(!istype(A.loc, type))
+ if(!A.loc)
+ return
+ A = A.loc
+
+ return A.loc
+
+/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank)
+ if(!SSticker.IsRoundInProgress() || QDELETED(character))
+ return
+ var/area/A = get_area(character)
+ deadchat_broadcast(" has arrived at the station at [A.name].", "[character.real_name] ([rank])", follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE)
+ if((!GLOB.announcement_systems.len) || (!character.mind))
+ return
+ if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role))
+ return
+
+ var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
+ announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common
+
+/proc/GetRedPart(const/hexa)
+ return hex2num(copytext(hexa, 2, 4))
+
+/proc/GetGreenPart(const/hexa)
+ return hex2num(copytext(hexa, 4, 6))
+
+/proc/GetBluePart(const/hexa)
+ return hex2num(copytext(hexa, 6, 8))
+
+/proc/lavaland_equipment_pressure_check(turf/T)
+ . = FALSE
+ if(!istype(T))
+ return
+ var/datum/gas_mixture/environment = T.return_air()
+ if(!istype(environment))
+ return
+ var/pressure = environment.return_pressure()
+ if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
+ . = TRUE
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index a108082be66c..ca4f5f7b8009 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -1,71 +1,71 @@
-//////////////////////////
-/////Initial Building/////
-//////////////////////////
-
-/proc/make_datum_references_lists()
- //hair
- init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list)
- //facial hair
- init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list)
- //underwear
- init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
- //undershirt
- init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f)
- //socks
- init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list)
- //bodypart accessories (blizzard intensifies)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.body_markings_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, GLOB.tails_list_lizard)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/lizard, GLOB.animated_tails_list_lizard)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, GLOB.tails_list_human)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/human, GLOB.animated_tails_list_human)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, GLOB.snouts_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/horns,GLOB.horns_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, GLOB.ears_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.wings_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/wings_open, GLOB.wings_open_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, GLOB.frills_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, GLOB.spines_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/spines_animated, GLOB.animated_spines_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/legs, GLOB.legs_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.r_wings_list,roundstart = TRUE)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/caps, GLOB.caps_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_list)
-
-
- //Species
- for(var/spath in subtypesof(/datum/species))
- var/datum/species/S = new spath()
- GLOB.species_list[S.id] = spath
-
- //Surgeries
- for(var/path in subtypesof(/datum/surgery))
- GLOB.surgeries_list += new path()
-
- //Materials
- for(var/path in subtypesof(/datum/material))
- var/datum/material/D = new path()
- GLOB.materials_list[D.id] = D
-
- GLOB.emote_list = init_emote_list()
-
-
- init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
-
-//creates every subtype of prototype (excluding prototype) and adds it to list L.
-//if no list/L is provided, one is created.
-/proc/init_subtypes(prototype, list/L)
- if(!istype(L))
- L = list()
- for(var/path in subtypesof(prototype))
- L += new path()
- return L
-
-//returns a list of paths to every subtype of prototype (excluding prototype)
-//if no list/L is provided, one is created.
-/proc/init_paths(prototype, list/L)
- if(!istype(L))
- L = list()
- for(var/path in subtypesof(prototype))
- L+= path
- return L
+//////////////////////////
+/////Initial Building/////
+//////////////////////////
+
+/proc/make_datum_references_lists()
+ //hair
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list)
+ //facial hair
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list)
+ //underwear
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
+ //undershirt
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f)
+ //socks
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list)
+ //bodypart accessories (blizzard intensifies)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.body_markings_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, GLOB.tails_list_lizard)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/lizard, GLOB.animated_tails_list_lizard)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, GLOB.tails_list_human)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/human, GLOB.animated_tails_list_human)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, GLOB.snouts_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/horns,GLOB.horns_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, GLOB.ears_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.wings_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/wings_open, GLOB.wings_open_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, GLOB.frills_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, GLOB.spines_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spines_animated, GLOB.animated_spines_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/legs, GLOB.legs_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, GLOB.r_wings_list,roundstart = TRUE)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/caps, GLOB.caps_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_list)
+
+
+ //Species
+ for(var/spath in subtypesof(/datum/species))
+ var/datum/species/S = new spath()
+ GLOB.species_list[S.id] = spath
+
+ //Surgeries
+ for(var/path in subtypesof(/datum/surgery))
+ GLOB.surgeries_list += new path()
+
+ //Materials
+ for(var/path in subtypesof(/datum/material))
+ var/datum/material/D = new path()
+ GLOB.materials_list[D.id] = D
+
+ GLOB.emote_list = init_emote_list()
+
+
+ init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
+
+//creates every subtype of prototype (excluding prototype) and adds it to list L.
+//if no list/L is provided, one is created.
+/proc/init_subtypes(prototype, list/L)
+ if(!istype(L))
+ L = list()
+ for(var/path in subtypesof(prototype))
+ L += new path()
+ return L
+
+//returns a list of paths to every subtype of prototype (excluding prototype)
+//if no list/L is provided, one is created.
+/proc/init_paths(prototype, list/L)
+ if(!istype(L))
+ L = list()
+ for(var/path in subtypesof(prototype))
+ L+= path
+ return L
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 7b71eea02047..6a1287a8f870 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1,1202 +1,1202 @@
-/*
-IconProcs README
-
-A BYOND library for manipulating icons and colors
-
-by Lummox JR
-
-version 1.0
-
-The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation
-routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners.
-
-CHANGING ICONS
-
-Several new procs have been added to the /icon datum to simplify working with icons. To use them,
-remember you first need to setup an /icon var like so:
-
-GLOBAL_DATUM_INIT(my_icon, /icon, new('iconfile.dmi'))
-
-icon/ChangeOpacity(amount = 1)
- A very common operation in DM is to try to make an icon more or less transparent. Making an icon more
- transparent is usually much easier than making it less so, however. This proc basically is a frontend
- for MapColors() which can change opacity any way you like, in much the same way that SetIntensity()
- can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half.
- If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque.
-icon/GrayScale()
- Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact.
-icon/ColorTone(tone)
- Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an
- RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect.
- See also the global ColorTone() proc.
-icon/MinColors(icon)
- The icon is blended with a second icon where the minimum of each RGB pixel is the result.
- Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon.
-icon/MaxColors(icon)
- The icon is blended with a second icon where the maximum of each RGB pixel is the result.
- Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon.
-icon/Opaque(background = "#000000")
- All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify.
-icon/BecomeAlphaMask()
- You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc.
- The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white.
-icon/AddAlphaMask(mask)
- The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque,
- the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent.
- Where the mask is translucent, the current icon becomes more transparent.
-icon/UseAlphaMask(mask, mode)
- Sometimes you may want to take the alpha values from one icon and use them on a different icon.
- This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change
- so it has the same colors as before but uses the mask for opacity.
-
-COLOR MANAGEMENT AND HSV
-
-RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value.
-
- * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to
- cyan to blue to magenta and back to red.
- * The saturation of a color is how much color is in it. A color with low saturation will be more gray,
- and with no saturation at all it is a shade of gray.
- * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark,
- and no value at all is black.
-
-Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three
-hex digits because it ranges from 0 to 0x5FF.
-
- * 0 to 0xFF - red to yellow
- * 0x100 to 0x1FF - yellow to green
- * 0x200 to 0x2FF - green to cyan
- * 0x300 to 0x3FF - cyan to blue
- * 0x400 to 0x4FF - blue to magenta
- * 0x500 to 0x5FF - magenta to red
-
-Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible),
-value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff".
-
-More than one HSV color can match the same RGB color.
-
-Here are some procs you can use for color management:
-
-ReadRGB(rgb)
- Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used
- that includes alpha, the list will have a fourth item for the alpha value.
-hsv(hue, sat, val, apha)
- Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa"
- format. Alpha is not included in the result if null.
-ReadHSV(rgb)
- Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that
- includes alpha, the list will have a fourth item for the alpha value.
-RGBtoHSV(rgb)
- Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff".
-HSVtoRGB(hsv)
- Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa".
-BlendRGB(rgb1, rgb2, amount)
- Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result;
- if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
- The returned value is an RGB or RGBA color.
-BlendHSV(hsv1, hsv2, amount)
- Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB
- blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1,
- the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well.
- The returned value is an HSV or HSVA color.
-BlendRGBasHSV(rgb1, rgb2, amount)
- Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form.
-HueToAngle(hue)
- Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue.
-AngleToHue(hue)
- Converts an angle to a hue in the valid range.
-RotateHue(hsv, angle)
- Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360.
- (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value
- as the original, but a different hue.
-GrayScale(rgb)
- Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string.
-ColorTone(rgb, tone)
- Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of
- using strict shades of gray. The tone value is an RGB color; any alpha value is ignored.
-*/
-
-/*
-Get Flat Icon DEMO by DarkCampainger
-
-This is a test for the get flat icon proc, modified approprietly for icons and their states.
-Probably not a good idea to run this unless you want to see how the proc works in detail.
-mob
- icon = 'old_or_unused.dmi'
- icon_state = "green"
-
- Login()
- // Testing image underlays
- underlays += image(icon='old_or_unused.dmi',icon_state="red")
- underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32)
- underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32)
-
- // Testing image overlays
- add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32))
- add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32))
- add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32))
-
- // Testing icon file overlays (defaults to mob's state)
- add_overlay('_flat_demoIcons2.dmi')
-
- // Testing icon_state overlays (defaults to mob's icon)
- add_overlay("white")
-
- // Testing dynamic icon overlays
- var/icon/I = icon('old_or_unused.dmi', icon_state="aqua")
- I.Shift(NORTH,16,1)
- add_overlay(I)
-
- // Testing dynamic image overlays
- I=image(icon=I,pixel_x = -32, pixel_y = 32)
- add_overlay(I)
-
- // Testing object types (and layers)
- add_overlay(/obj/effect/overlayTest)
-
- loc = locate (10,10,1)
- verb
- Browse_Icon()
- set name = "1. Browse Icon"
- // Give it a name for the cache
- var/iconName = "[ckey(src.name)]_flattened.dmi"
- // Send the icon to src's local cache
- src<