code:
/proc/ui_equip_position(mob/M)
	return "WEST+10,SOUTH+1"

/proc/ui_swaphand_position(mob/M, which = 1)
	return "WEST+10,SOUTH+1"

/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5)
	var/x_off = (i % 2)
	return"WEST+[10+x_off],SOUTH"

//Non-widescreen defines
#define ui_boxstorage1 "WEST,SOUTH+1"
#define ui_boxstorage2 "WEST+1,SOUTH+1"
#define ui_boxgloves "WEST+4,SOUTH"
#define ui_boxneck "WEST+5,SOUTH"
#define ui_boxglasses "WEST+6,SOUTH"
#define ui_boxbookmark_on "WEST+7,SOUTH"
#define ui_boxspecialattack "EAST-3,SOUTH"

//Middle left indicators
#define ui_lingchemdisplay "WEST,CENTER-1"
#define ui_lingstingdisplay "WEST:6,CENTER-3"

//Basically unused
#define ui_combo "CENTER+4,SOUTH"
#define ui_inventory "WEST,SOUTH"
#define ui_give "EAST-1,CENTER+5"
#define ui_safety "EAST-1,CENTER+0"
#define ui_healthdoll "EAST-1,SOUTH+2"
#define ui_sprintbuffer "EAST-1,SOUTH"

//Screentip
#define ui_screentip "CENTER-3,NORTH"

//Lower leftmost right
#define ui_specialattack "EAST-7,SOUTH"
#define ui_combat_style "EAST-6,SOUTH"
#define ui_combat_toggle "EAST-5,SOUTH"
#define ui_intents "EAST-4,SOUTH"
#define ui_acti_alt "EAST,SOUTH" //alternative intent switcher for when the interface is hidden (F12)
#define ui_wield "EAST-3,SOUTH"
#define ui_resist "EAST-3,SOUTH"
#define ui_sprint "EAST-2,SOUTH"
#define ui_movi "EAST-2,SOUTH"
#define ui_dodge_parry "EAST-1,SOUTH"

//Middle right (alerts)
#define ui_alert1 "EAST-2,SOUTH+1"
#define ui_alert2 "EAST-2,SOUTH+2"
#define ui_alert3 "EAST-2,SOUTH+3"
#define ui_alert4 "EAST-2,SOUTH+4"
#define ui_alert5 "EAST-2,SOUTH+5"
#define ui_alert6 "EAST-2,SOUTH+6"

//Lower middle right
#define ui_filler "EAST-1,SOUTH+4"
#define ui_throw "EAST-1,SOUTH+3"
#define ui_drop "EAST-1,SOUTH+3"
#define ui_sleep "EAST-1,SOUTH+2"
#define ui_teach "EAST-1,SOUTH+2"
#define ui_pull "EAST-1,SOUTH+1"
#define ui_rest "EAST-1,SOUTH+1"

//Right (status indicators)
#define ui_stats "EAST,SOUTH+14"
#define ui_spacesuit "EAST,SOUTH+13"
#define ui_surrender "EAST,SOUTH+13"
#define ui_pressure "EAST,SOUTH+12"
#define ui_internal "EAST,SOUTH+12"
#define ui_fixeye "EAST,SOUTH+11"
#define ui_lookup "EAST,SOUTH+10"
#define ui_lookdown "EAST,SOUTH+10"
#define ui_fatigue "EAST,SOUTH+9"
#define ui_nutrition "EAST,SOUTH+8"
#define ui_hydration "EAST,SOUTH+8"
#define ui_mood "EAST,SOUTH+7"
#define ui_skills "EAST,SOUTH+6"
#define ui_crafting "EAST,SOUTH+6"
#define ui_language_menu "EAST,SOUTH+6"
#define ui_building "EAST,SOUTH+6"
#define ui_pain	"EAST,SOUTH+5"
#define ui_temperature "EAST,SOUTH+4"
#define ui_bodytemperature "EAST,SOUTH+4"
#define ui_pulse "EAST,SOUTH+3"
#define ui_breath "EAST,SOUTH+3"
#define ui_enhancesel "EAST,SOUTH+2"
#define ui_zonesel "EAST,SOUTH"

//Middle of the screen (we use 22x16 so everything is in real wacky hud locations)
#define ui_middleofscreen "WEST+2,SOUTH+1"
#define ui_fov "WEST+3,SOUTH+1"

//Expand inventory bookmark
#define ui_bookmark_off "WEST,SOUTH+1"
#define ui_bookmark_on "WEST+4,SOUTH+1"

//Pop-up inventory
#define ui_shoes "WEST,SOUTH"
#define ui_iclothing "WEST+1,SOUTH"
#define ui_oclothing "WEST+2,SOUTH"
#define ui_gloves "WEST+3,SOUTH"
#define ui_mask "WEST+4,SOUTH"
#define ui_head "WEST+5,SOUTH"
#define ui_id "WEST+6,SOUTH"
#define ui_belt "WEST+7,SOUTH"
#define ui_sstore1 "WEST+8,SOUTH"
#define ui_back "WEST+9,SOUTH"
#define ui_storage1 "WEST+12,SOUTH"
#define ui_storage2 "WEST+13,SOUTH"
#define ui_neck "WEST,SOUTH+1"
#define ui_glasses "WEST+1,SOUTH+1"
#define ui_ears "WEST+2,SOUTH+1"
#define ui_ears_extra "WEST+3,SOUTH+1"
#define ui_wrist_right "WEST+4,SOUTH+1"
#define ui_wrist_left "WEST+5,SOUTH+1"

//Generic living
#define ui_living_pull "EAST-1:28,CENTER-3:15"
#define ui_living_healthdoll "EAST-1:28,CENTER-1:15"

//Monkeys
#define ui_monkey_head "CENTER-5:13,SOUTH:5"
#define ui_monkey_mask "CENTER-4:14,SOUTH:5"
#define ui_monkey_neck "CENTER-3:15,SOUTH:5"
#define ui_monkey_back "CENTER-2:16,SOUTH:5"

//Drones
#define ui_drone_drop "CENTER+1:18,SOUTH:5"
#define ui_drone_pull "CENTER+2:2,SOUTH:5"
#define ui_drone_storage "CENTER-2:14,SOUTH:5"
#define ui_drone_head "CENTER-3:14,SOUTH:5"

//Cyborgs
#define ui_borg_health "EAST-1:28,CENTER-1:15"
#define ui_borg_pull "EAST-2:26,SOUTH+1:7"
#define ui_borg_radio "EAST-1:28,SOUTH+1:7"
#define ui_borg_intents "EAST-2:26,SOUTH:5"
#define ui_borg_lamp "CENTER-3:16, SOUTH:5"
#define ui_borg_tablet "CENTER-4:16, SOUTH:5"
#define ui_inv1 "CENTER-2:16,SOUTH:5"
#define ui_inv2 "CENTER-1  :16,SOUTH:5"
#define ui_inv3 "CENTER  :16,SOUTH:5"
#define ui_borg_module "CENTER+1:16,SOUTH:5"
#define ui_borg_store "CENTER+2:16,SOUTH:5"
#define ui_borg_camera "CENTER+3:21,SOUTH:5"
#define ui_borg_alerts "CENTER+4:21,SOUTH:5"
#define ui_borg_language_menu "CENTER+4:19,SOUTH+1:6"

//Aliens
#define ui_alien_health "EAST,CENTER-1"
#define ui_alienplasmadisplay "EAST,CENTER-2"
#define ui_alien_queen_finder "EAST,CENTER-3"
#define ui_alien_storage_r "CENTER+1,SOUTH"
#define ui_alien_language_menu "EAST-4,SOUTH"

//AI
#define ui_ai_core "SOUTH:6,WEST"
#define ui_ai_camera_list "SOUTH:6,WEST+1"
#define ui_ai_track_with_camera "SOUTH:6,WEST+2"
#define ui_ai_camera_light "SOUTH:6,WEST+3"
#define ui_ai_crew_monitor "SOUTH:6,WEST+4"
#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
#define ui_ai_alerts "SOUTH:6,WEST+6"
#define ui_ai_announcement "SOUTH:6,WEST+7"
#define ui_ai_shuttle "SOUTH:6,WEST+8"
#define ui_ai_state_laws "SOUTH:6,WEST+9"
#define ui_ai_pda_send "SOUTH:6,WEST+10"
#define ui_ai_pda_log "SOUTH:6,WEST+11"
#define ui_ai_take_picture "SOUTH:6,WEST+12"
#define ui_ai_view_images "SOUTH:6,WEST+13"
#define ui_ai_sensor "SOUTH:6,WEST+14"
#define ui_ai_multicam "SOUTH+1:6,WEST+13"
#define ui_ai_add_multicam "SOUTH+1:6,WEST+14"
#define ui_ai_language_menu "SOUTH+1:8,WEST+11:30"

//pAI
#define ui_pai_software "SOUTH,WEST"
#define ui_pai_shell "SOUTH,WEST+1"
#define ui_pai_chassis "SOUTH,WEST+2"
#define ui_pai_rest "SOUTH,WEST+3"
#define ui_pai_light "SOUTH,WEST+4"
#define ui_pai_newscaster "SOUTH,WEST+5"
#define ui_pai_host_monitor "SOUTH,WEST+6"
#define ui_pai_crew_manifest "SOUTH,WEST+7"
#define ui_pai_state_laws "SOUTH,WEST+8"
#define ui_pai_pda_send "SOUTH,WEST+9"
#define ui_pai_pda_log "SOUTH,WEST+10"
#define ui_pai_internal_gps "SOUTH,WEST+11"
#define ui_pai_take_picture "SOUTH,WEST+12"
#define ui_pai_view_images "SOUTH,WEST+13"
#define ui_pai_radio "SOUTH,WEST+14"
#define ui_pai_language_menu "SOUTH+1,WEST+13"

//Ghosts
#define ui_ghost_spawners_menu "SOUTH:6,CENTER-3:24"
#define ui_ghost_orbit "SOUTH:6,CENTER-2:24"
#define ui_ghost_reenter_corpse "SOUTH:6,CENTER-1:24"
#define ui_ghost_teleport "SOUTH:6,CENTER:24"
#define ui_ghost_pai "SOUTH: 6, CENTER+1:24"
#define ui_ghost_minigames "SOUTH: 6, CENTER+2:24"
#define ui_ghost_language_menu "SOUTH: 22, CENTER+3:8"

//Blobbernauts
#define ui_blobbernaut_overmind_health "EAST,CENTER"

//Families
#define ui_wanted_lvl "WEST,NORTH-1"


code:
#undef MAX_S_OBJ

#define SMOOTH_GROUP_LIQUID S_OBJ(71) ///atom/movable/liquid

#define SMOOTH_GROUP_TRAM S_OBJ(72)

#define MAX_S_OBJ SMOOTH_GROUP_TRAM

repo_name: AnalWerewolf/sundown

license: mit


code:
#define MUTATIONS_LAYER 37 //Mutations that appear behind the body - headglows, cold resistance glow, etc
#define BODY_BEHIND_LAYER 36 //Certain mutantrace features (tail when looking south) that must appear behind the body parts
#define BODYPARTS_LAYER 35 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag
#define BODY_ADJ_LAYER 34 //certain mutantrace features (snout, body markings) that must appear above the body parts
#define BODY_LAYER 33 //underwear, undershirts, socks, eyes, lips(makeup)
#define FRONT_MUTATIONS_LAYER 32 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes)
#define DAMAGE_LAYER 31 //damage indicators (cuts and burns)
#define UNIFORM_LAYER 30
#define ID_LAYER 29
#define ID_CARD_LAYER 28
#define MEDICINE_LAYER 27 //Medicine, like gauze and tourniquets
#define HANDS_PART_LAYER 26
#define HANDS_ADJ_LAYER 25
#define UPPER_DAMAGE_LAYER 24 //damage indicators for the hands
#define UPPER_MEDICINE_LAYER 23 //Medicine, like gauze and tourniquets, for the hands
#define GLOVES_LAYER 22
#define SHOES_LAYER 21
#define EARS_LAYER 20
#define SUIT_LAYER 19
#define GLASSES_LAYER 18
#define BELT_LAYER 17 //Possibly make this an overlay of somethign required to wear a belt?
#define SUIT_STORE_LAYER 16
#define NECK_LAYER 15
#define BACK_LAYER 14
#define HAIR_LAYER 13 //TODO: make part of head layer?
#define FACEMASK_LAYER 12
#define HEAD_LAYER 11
#define HANDCUFF_LAYER 10
#define LEGCUFF_LAYER 9
#define HANDS_LAYER 8
#define BODY_FRONT_LAYER 7 // Usually used for mutant bodyparts that need to be in front of everything else (e.g. cat ears)
#define ABOVE_BODY_FRONT_GLASSES_LAYER 6 // For the special glasses that actually require to be above the hair (e.g. lifted welding goggles)
#define ABOVE_BODY_FRONT_HEAD_LAYER 5 // For the rare cases where something on the head needs to be above everything else (e.g. flowers)
#define ARTERY_LAYER 4
#define SMELL_LAYER 3
#define HALO_LAYER 2 //blood cult ascended halo, because there's currently no better solution for adding/removing
#define FIRE_LAYER 1 //If you're on fire

#define TOTAL_LAYERS MUTATIONS_LAYER //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;


code:
//intent defines
#define INTENT_HELP "help"
#define INTENT_GRAB "grab"
#define INTENT_DISARM "disarm"
#define INTENT_HARM "harm"
#define DEFAULT_INTENTS_LIST list(INTENT_HELP, INTENT_GRAB, INTENT_DISARM, INTENT_HARM)

//NOTE: INTENT_HOTKEY_* defines are not actual intents!
//they are here to support hotkeys
#define INTENT_HOTKEY_LEFT  "left"
#define INTENT_HOTKEY_RIGHT "right"

//harm intent checks
#define IS_HARM_INTENT(mob, modifiers) (ishuman(mob) ? (mob.a_intent == INTENT_HARM) : (mob.combat_mode && !LAZYACCESS(modifiers, RIGHT_CLICK)))
#define IS_NOT_HARM_INTENT(mob, modifiers) (ishuman(mob) ? (mob.a_intent != INTENT_HARM) : (!mob.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK)))
#define IS_HELP_INTENT(mob, modifiers) (ishuman(mob) ? (mob.a_intent == INTENT_HELP) : (!mob.combat_mode && !LAZYACCESS(modifiers, RIGHT_CLICK)))
#define IS_DISARM_INTENT(mob, modifiers) (ishuman(mob) ? (mob.a_intent == INTENT_DISARM) : (LAZYACCESS(modifiers, RIGHT_CLICK)))
#define IS_GRAB_INTENT(mob, modifiers) (ishuman(mob) ? (mob.a_intent == INTENT_GRAB) : (mob.combat_mode))

//intent changes
#define SET_HARM_INTENT(mob) (ishuman(mob) ? (mob.a_intent_change(INTENT_HARM)) : (mob.set_combat_mode(TRUE)))
#define SET_HELP_INTENT(mob) (ishuman(mob) ? (mob.a_intent_change(INTENT_HELP)) : (mob.set_combat_mode(FALSE)))


code:
/// Default interaction cooldown, can be changed on each datum
#define INTERACTION_COOLDOWN 0.5 SECONDS

#define INTERACT_SELF "self"
#define INTERACT_OTHER "other"
#define INTERACT_BOTH "both"

// ~interaction flags
/// Respects interaction_cooldown
#define INTERACTION_RESPECT_COOLDOWN (1<<0)
/// Respects sex_interaction_cooldown
#define INTERACTION_RESPECT_SEX_COOLDOWN (1<<1)
/// Interaction will call climax() on the user, if applicable
#define INTERACTION_USER_CLIMAX (1<<2)
/// Interaction will call climax() on the target, if applicable
#define INTERACTION_TARGET_CLIMAX (1<<3)
/// Interaction will call lust_message() on the user, if applicable
#define INTERACTION_USER_LUST (1<<4)
/// Interaction will call lust_message() on the target, if applicable
#define INTERACTION_TARGET_LUST (1<<5)
/// Use audible_message instead of visible_message
#define INTERACTION_AUDIBLE (1<<6)
/// User and target must be adjacent
#define INTERACTION_NEEDS_PHYSICAL_CONTACT (1<<7)

// ~interaction categories
#define INTERACTION_CATEGORY_UNFRIENDLY "Unfriendly"
#define INTERACTION_CATEGORY_FRIENDLY "Friendly"
#define INTERACTION_CATEGORY_ROMANTIC "Romantic"
#define INTERACTION_CATEGORY_FORBIDDEN_FRUITS "Forbidden Fruits"

// ~body zone or organ that needs to be exposed on interactions, avoid using this in zones in favor of organs
#define INTERACTION_ZONE_EXISTS (1<<0)
#define INTERACTION_ZONE_COVERED (1<<1)
#define INTERACTION_ZONE_UNCOVERED (1<<1)

/// Factor at which genitals lose their proximity to cumming, per second
#define DELUST_FACTOR 0.2

/// Once we hit this amount of lust, we cum
#define LUST_CLIMAX 300

/// No lust increase (ass slap)
#define LUST_INCREASE_NONE 0
/// Low lust increase on sex (thigh smother, deep kiss, etc)
#define LUST_INCREASE_LOW 1
/// Normal lust increase on sex
#define LUST_INCREASE_NORMAL 10

#define INTERACTION_CATEGORY_ORDER list(\
			INTERACTION_CATEGORY_UNFRIENDLY, \
			INTERACTION_CATEGORY_FRIENDLY, \
			INTERACTION_CATEGORY_ROMANTIC, \
			INTERACTION_CATEGORY_FORBIDDEN_FRUITS, \
			)


code:
//flags for outfits that have mutant variants: Most of these require additional sprites to work.
#define STYLE_DIGITIGRADE (1<<0) //jumpsuits, suits and shoes
#define STYLE_MUZZLE (1<<1) //hats or masks
#define STYLE_TAUR_SNAKE (1<<2) //snake taur friendly suits
#define STYLE_TAUR_PAW (1<<3) //paw taur friendly suits
#define STYLE_TAUR_HOOF (1<<4) //hoof taur friendly suits
#define STYLE_TAUR_ALL (STYLE_TAUR_SNAKE|STYLE_TAUR_PAW|STYLE_TAUR_HOOF)

//ITEM INVENTORY SLOT BITMASKS
/// Suit slot (armors, costumes, space suits, etc.)
#define ITEM_SLOT_OCLOTHING (1<<0)
/// Jumpsuit slot
#define ITEM_SLOT_ICLOTHING (1<<1)
/// Glove slot
#define ITEM_SLOT_GLOVES (1<<2)
/// Glasses slot
#define ITEM_SLOT_EYES (1<<3)
/// Left ear slot
#define ITEM_SLOT_LEAR (1<<4)
/// Right ear slot
#define ITEM_SLOT_REAR (1<<5)
/// Mask slot
#define ITEM_SLOT_MASK (1<<6)
/// Head slot (helmets, hats, etc.)
#define ITEM_SLOT_HEAD (1<<7)
/// Shoe slot
#define ITEM_SLOT_FEET (1<<8)
/// ID slot
#define ITEM_SLOT_ID (1<<9)
/// Belt slot
#define ITEM_SLOT_BELT (1<<10)
/// Back slot
#define ITEM_SLOT_BACK (1<<11)
/// Dextrous simplemob "hands" (used for Drones and Dextrous Guardians)
#define ITEM_SLOT_DEX_STORAGE (1<<12)
/// Neck slot (ties, bedsheets, scarves)
#define ITEM_SLOT_NECK (1<<13)
/// A character's hand slots
#define ITEM_SLOT_HANDS (1<<14)
/// Inside of a character's backpack
#define ITEM_SLOT_BACKPACK (1<<15)
/// Suit Storage slot
#define ITEM_SLOT_SUITSTORE (1<<16)
/// Left Pocket slot
#define ITEM_SLOT_LPOCKET (1<<17)
/// Right Pocket slot
#define ITEM_SLOT_RPOCKET (1<<18)
/// Handcuff slot
#define ITEM_SLOT_HANDCUFFED (1<<19)
/// Legcuff slot (bolas, beartraps)
#define ITEM_SLOT_LEGCUFFED (1<<20)

/// Total amount of slots
#define SLOTS_AMT 21 // Keep this up to date!

//SLOT GROUP HELPERS
#define ITEM_SLOT_POCKETS (ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET)
#define ITEM_SLOT_EARS (ITEM_SLOT_LEAR|ITEM_SLOT_REAR)

/// Item slots that if you equip an item on, it'll try to filthify you
#define DIRTY_ITEM_SLOTS (ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING|ITEM_SLOT_GLOVES|ITEM_SLOT_EYES|ITEM_SLOT_LEAR|ITEM_SLOT_REAR|ITEM_SLOT_HEAD|ITEM_SLOT_FEET|ITEM_SLOT_BELT|ITEM_SLOT_BACK)


code:
/// LOL
#define DEFAULT_CLIENT_COUNTRY "Niger"


code: #define isliquid(A) istype(A, /atom/movable/liquid)


code:
#define JOB_UNAVAILABLE_QUIRK 6
#define JOB_UNAVAILABLE_SPECIES 7
#define JOB_UNAVAILABLE_LANGUAGE 7

#define SEC_RESTRICTED_QUIRKS "Blind" = TRUE, "Brain Tumor" = TRUE, "Deaf" = TRUE, "Paraplegic" = TRUE, "Mute" = TRUE, "Foreigner" = TRUE, "Pacifist" = TRUE, "Chunky Fingers" = TRUE
#define HEAD_RESTRICTED_QUIRKS "Blind" = TRUE, "Deaf" = TRUE, "Mute" = TRUE, "Foreigner" = TRUE, "Chunky Fingers" = TRUE
#define TECH_RESTRICTED_QUIRKS "Chunky Fingers" = TRUE


code:
//Carbon
#define COMSIG_KB_CARBON_SELECTHELPINTENT_DOWN "keybinding_carbon_selecthelpintent_down"
#define COMSIG_KB_CARBON_SELECTDISARMINTENT_DOWN "keybinding_carbon_selectdisarmintent_down"
#define COMSIG_KB_CARBON_SELECTGRABINTENT_DOWN "keybinding_carbon_selectgrabintent_down"
#define COMSIG_KB_CARBON_SELECTHARMINTENT_DOWN "keybinding_carbon_selectharmintent_down"
#define COMSIG_KB_FIXEYE_DOWN "keybinding_fixeye_down"
#define COMSIG_KB_CARBON_SPRINT_DOWN "keybinding_carbon_sprint_down"
#define COMSIG_KB_CARBON_WIELD_DOWN "keybinding_carbon_wield_down"

//Communication
#define COMSIG_KB_CLIENT_WHISPER_DOWN "keybinding_client_whisper_down"


code:
//Defines for atom layers and planes
//KEEP THESE IN A NICE ASCENDING ORDER, PLEASE

//NEVER HAVE ANYTHING BELOW THIS PLANE ADJUST IF YOU NEED MORE SPACE
#define LOWEST_EVER_PLANE -200

#define CLICKCATCHER_PLANE -99

#define PLANE_SPACE -95
#define PLANE_SPACE_RENDER_TARGET "PLANE_SPACE"
#define PLANE_SPACE_PARALLAX -90
#define PLANE_SPACE_PARALLAX_RENDER_TARGET "PLANE_SPACE_PARALLAX"

#define GRAVITY_PULSE_PLANE -11
#define GRAVITY_PULSE_RENDER_TARGET "*GRAVPULSE_RENDER_TARGET"

#define OPENSPACE_LAYER 600 //Openspace layer over all
#define OPENSPACE_PLANE -10 //Openspace plane below all turfs
#define OPENSPACE_RENDER_TARGET "OPENSPACE_PLANE"
#define OPENSPACE_BACKDROP_PLANE -9 //Black square just over openspace plane to guaranteed cover all in openspace turf
#define OPENSPACE_BACKDROP_RENDER_TARGET "OPENSPACE_BACKDROP_PLANE"

#define FLOOR_PLANE -8
#define GAME_PLANE -7
#define MOB_PLANE -6
#define OVER_MOB_PLANE -5
#define POLLUTION_PLANE -4

/// Used to well... block FoV
#define FIELD_OF_VISION_BLOCKER_PLANE -3
#define FIELD_OF_VISION_BLOCKER_RENDER_TARGET "FIELD_OF_VISION_BLOCKER_PLANE"
/// Yeah, FoV does require quite a few planes to work with 513 filters to a decent degree.
#define FIELD_OF_VISION_MASK_PLANE -2
#define FIELD_OF_VISION_MASK_RENDER_TARGET "*FIELD_OF_VISION_PLANE"
/// Used to place the visual (not the mask) shadow cone above any other floor plane stuff.
#define FIELD_OF_VISION_VISUAL_PLANE -1
#define FIELD_OF_VISION_VISUAL_LAYER 17
#define FIELD_OF_VISION_VISUAL_RENDER_TARGET "FIELD_OF_VISION_VISUAL_PLANE"

#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
#define BLACKNESS_PLANE_RENDER_TARGET "BLACKNESS_PLANE"

#define SPACE_LAYER 1.8
//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define
#define CULT_OVERLAY_LAYER 2.01
#define MID_TURF_LAYER 2.02
#define HIGH_TURF_LAYER 2.03
#define TURF_PLATING_DECAL_LAYER 2.031
#define TURF_DECAL_LAYER 2.039 //Makes turf decals appear in DM how they will look inworld.
#define ABOVE_OPEN_TURF_LAYER 2.04
#define CLOSED_TURF_LAYER 2.05
#define BULLET_HOLE_LAYER 2.06
#define ABOVE_NORMAL_TURF_LAYER 2.08
#define LATTICE_LAYER 2.2
#define DISPOSAL_PIPE_LAYER 2.3
#define GAS_PIPE_HIDDEN_LAYER 2.35 //layer = initial(layer) + piping_layer / 1000 in atmospherics/update_icon() to determine order of pipe overlap
#define WIRE_LAYER 2.4
#define WIRE_BRIDGE_LAYER 2.44
#define WIRE_TERMINAL_LAYER 2.45
#define GAS_SCRUBBER_LAYER 2.46
#define GAS_PIPE_VISIBLE_LAYER 2.47 //layer = initial(layer) + piping_layer / 1000 in atmospherics/update_icon() to determine order of pipe overlap
#define GAS_FILTER_LAYER 2.48
#define GAS_PUMP_LAYER 2.49
#define LOW_OBJ_LAYER 2.5
///catwalk overlay of /turf/open/floor/plating/catwalk_floor
#define CATWALK_LAYER 2.51
#define LOW_SIGIL_LAYER 2.52
#define SIGIL_LAYER 2.54
#define HIGH_PIPE_LAYER 2.55
#define HIGH_SIGIL_LAYER 2.56

#define BELOW_OPEN_DOOR_LAYER 2.6
#define BLASTDOOR_LAYER 2.65
#define OPEN_DOOR_LAYER 2.7
#define DOOR_HELPER_LAYER 2.71 //keep this above OPEN_DOOR_LAYER
#define LIQUID_LAYER 2.72
#define PROJECTILE_HIT_THRESHHOLD_LAYER 2.75 //projectiles won't hit objects at or below this layer if possible
#define TABLE_LAYER 2.8
#define GATEWAY_UNDERLAY_LAYER 2.85
#define BELOW_OBJ_LAYER 2.9
#define LOW_ITEM_LAYER 2.95
//#define OBJ_LAYER 3 //For easy recordkeeping; this is a byond define
#define CLOSED_BLASTDOOR_LAYER 3.05
#define CLOSED_DOOR_LAYER 3.1
#define CLOSED_FIREDOOR_LAYER 3.11
#define SHUTTER_LAYER 3.12 // HERE BE DRAGONS
#define ABOVE_OBJ_LAYER 3.2
#define ABOVE_WINDOW_LAYER 3.3
#define SIGN_LAYER 3.4
#define CORGI_ASS_PIN_LAYER 3.41
#define NOT_HIGH_OBJ_LAYER 3.5
#define HIGH_OBJ_LAYER 3.6

#define BELOW_MOB_LAYER 3.7
#define LYING_MOB_LAYER 3.8
#define MOB_BELOW_PIGGYBACK_LAYER 3.94
//#define MOB_LAYER 4 //For easy recordkeeping; this is a byond define
#define MOB_SHIELD_LAYER 4.01
#define MOB_ABOVE_PIGGYBACK_LAYER 4.06
#define ABOVE_MOB_LAYER 4.1
#define WALL_OBJ_LAYER 4.25
#define EDGED_TURF_LAYER 4.3
#define ON_EDGED_TURF_LAYER 4.35
#define LARGE_MOB_LAYER 4.4
#define ABOVE_ALL_MOB_LAYER 4.5

#define SPACEVINE_LAYER 4.8
#define SPACEVINE_MOB_LAYER 4.9
//#define FLY_LAYER 5 //For easy recordkeeping; this is a byond define
#define GASFIRE_LAYER 5.05
#define RIPPLE_LAYER 5.1

#define LANDMARK_PLANE 50
#define LOW_LANDMARK_LAYER 1
#define MID_LANDMARK_LAYER 2
#define HIGH_LANDMARK_LAYER 3

#define AREA_PLANE 60
#define MASSIVE_OBJ_PLANE 70
#define GHOST_PLANE 80
#define POINT_PLANE 90

#define RAD_TEXT_PLANE 90

//---------- LIGHTING -------------
///Normal 1 per turf dynamic lighting objects
#define LIGHTING_PLANE 100
#define LIGHTING_RENDER_TARGET "LIGHT_PLANE"

///Lighting objects that are "free floating"
#define O_LIGHTING_VISUAL_PLANE 110
#define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE"

///Things that should render ignoring lighting
#define ABOVE_LIGHTING_PLANE 120
#define ABOVE_LIGHTING_RENDER_TARGET "ABOVE_LIGHTING_PLANE"

#define LIGHTING_PRIMARY_LAYER 15	//The layer for the main lights of the station
#define LIGHTING_PRIMARY_DIMMER_LAYER 15.1	//The layer that dims the main lights of the station
#define LIGHTING_SECONDARY_LAYER 16	//The colourful, usually small lights that go on top

///visibility + hiding of things outside of light source range
#define BYOND_LIGHTING_PLANE 130
#define BYOND_LIGHTING_RENDER_TARGET "BYOND_LIGHTING_PLANE"

//---------- EMISSIVES -------------
//Layering order of these is not particularly meaningful.
//Important part is the seperation of the planes for control via plane_master

///This plane masks out lighting to create an "emissive" effect, ie for glowing lights in otherwise dark areas
#define EMISSIVE_PLANE 150
#define EMISSIVE_RENDER_TARGET "*EMISSIVE_PLANE"

///This plane masks the emissive plane to "block" it. Byond is wacky, this is the only way to get things to look like they're actually blocking said glowing lights.
#define EMISSIVE_BLOCKER_PLANE 160
#define EMISSIVE_BLOCKER_RENDER_TARGET "*EMISSIVE_BLOCKER_PLANE"

///This plane is "unblockable" emissives. It does the same thing as the emissive plane but isn't masked by the emissive blocker plane. Use for on-mob and movable emissives.
#define EMISSIVE_UNBLOCKABLE_PLANE 170
#define EMISSIVE_UNBLOCKABLE_RENDER_TARGET "*EMISSIVE_UNBLOCKABLE_PLANE"

///---------------- MISC -----------------------

///AI Camera Static
#define CAMERA_STATIC_PLANE 200
#define CAMERA_STATIC_RENDER_TARGET "CAMERA_STATIC_PLANE"

///Popup Chat Messages
#define RUNECHAT_PLANE 250
#define RUNECHAT_RENDER_TARGET "RUNECHAT_PLANE"

///Plane for balloon text (text that fades up)
#define BALLOON_CHAT_PLANE 251

///Plane for sound hints
#define SOUND_HINT_PLANE 252

///Debug Atmos Overlays
#define ATMOS_GROUP_PLANE 450

///--------------- FULLSCREEN IMAGES ------------
#define FULLSCREEN_PLANE 500
#define FULLSCREEN_RENDER_TARGET "FULLSCREEN_PLANE"
#define FLASH_LAYER 1
#define FULLSCREEN_LAYER 2
#define UI_DAMAGE_LAYER 3
#define BLIND_LAYER 4
#define CRIT_LAYER 5
#define CURSE_LAYER 6
#define PAIN_FLASH_LAYER 7
#define NOISE_LAYER 8

//-------------------- Rendering ---------------------
#define RENDER_PLANE_GAME 990
#define RENDER_PLANE_NON_GAME 995
#define RENDER_PLANE_MASTER 999

//-------------------- HUD ---------------------
//HUD layer defines
#define HUD_PLANE 1000
#define HUD_RENDER_TARGET "HUD_PLANE"
#define ABOVE_HUD_PLANE 1100
#define ABOVE_HUD_RENDER_TARGET "ABOVE_HUD_PLANE"

#define RADIAL_BACKGROUND_LAYER 0
///1000 is an unimportant number, it's just to normalize copied layers
#define RADIAL_CONTENT_LAYER 1000

#define ADMIN_POPUP_LAYER 1

///Plane of the "splash" icon used that shows on the lobby screen. Nothing should ever be above this.
#define SPLASHSCREEN_PLANE 9999
#define SPLASHSCREEN_RENDER_TARGET "SPLASHSCREEN_PLANE"

#define LOBBY_BACKGROUND_LAYER 3
#define LOBBY_BUTTON_LAYER 4

///cinematics are "below" the splash screen
#define CINEMATIC_LAYER -1

///Plane master controller keys
#define PLANE_MASTERS_GAME "plane_masters_game"


code:
//Delta needed for the splash effect to be made in 1 go
#define WATER_HEIGHT_DIFFERENCE_SPLASH 7
//Chance to play if the delta is satisfied
#define WATER_HEIGHT_DIFFERENCE_SOUND_CHANCE 50

#define REQUIRED_EVAPORATION_PROCESSES 60
#define EVAPORATION_CHANCE 65

#define REQUIRED_FIRE_PROCESSES 4
#define REQUIRED_FIRE_POWER_PER_UNIT 5

#define PARTIAL_TRANSFER_AMOUNT 0.3

#define LIQUID_MUTUAL_SHARE 1
#define LIQUID_NOT_MUTUAL_SHARE 2

#define LIQUID_GIVER (1<<0)
#define LIQUID_TAKER (1<<1)

//Required amount of a reagent to be simulated on turf exposures from liquids (to prevent gaming the system with cheap dillutions)
#define LIQUID_REAGENT_THRESHOLD_TURF_EXPOSURE 5

//Threshold at which the difference of height makes us need to climb/blocks movement/allows to fall down
#define TURF_HEIGHT_BLOCK_THRESHOLD 20

#define LIQUID_HEIGHT_DIVISOR 10

#define ONE_LIQUIDS_HEIGHT LIQUID_HEIGHT_DIVISOR

#define LIQUID_ATTRITION_TO_STOP_ACTIVITY 2

//Percieved heat capacity for calculations with atmos sharing
#define REAGENT_HEAT_CAPACITY 5

//Add this to the liquid state if lying down
#define LYING_DOWN_SUBMERGEMENT_STATE_BONUS	2

#define LIQUID_STATE_PUDDLE	1
#define LIQUID_STATE_ANKLES	2
#define LIQUID_STATE_WAIST 3
#define LIQUID_STATE_SHOULDERS 4
#define LIQUID_STATE_FULLTILE 5
#define TOTAL_LIQUID_STATES 5

#define LIQUID_STATE_FOR_HEAT_EXCHANGERS LIQUID_STATE_WAIST

#define LIQUID_ANKLES_LEVEL_HEIGHT 8
#define LIQUID_WAIST_LEVEL_HEIGHT 20
#define LIQUID_SHOULDERS_LEVEL_HEIGHT 30
#define LIQUID_FULLTILE_LEVEL_HEIGHT 40

#define LIQUID_FIRE_STATE_NONE		0
#define LIQUID_FIRE_STATE_SMALL		1
#define LIQUID_FIRE_STATE_MILD		2
#define LIQUID_FIRE_STATE_MEDIUM	3
#define LIQUID_FIRE_STATE_HUGE		4
#define LIQUID_FIRE_STATE_INFERNO	5

//Threshold at which we "choke" on the water, instead of holding our breath
#define OXYGEN_DAMAGE_CHOKING_THRESHOLD 15

#define IMMUTABLE_LIQUID_SHARE 1

#define LIQUID_RECURSIVE_LOOP_SAFETY 100 //Hundred loops at maximum for adjacency checking

//Height at which we consider the tile "full" and dont drop liquids on it from the upper Z level
#define LIQUID_HEIGHT_CONSIDER_FULL_TILE 50

#define SSLIQUIDS_RUN_TYPE_TURFS 1
#define SSLIQUIDS_RUN_TYPE_GROUPS 2
#define SSLIQUIDS_RUN_TYPE_IMMUTABLES 3
#define SSLIQUIDS_RUN_TYPE_EVAPORATION 4
#define SSLIQUIDS_RUN_TYPE_FIRE 5

#define LIQUIDS_GROUP_DECAY_TIME 3

//Scaled with how much a person is submerged
#define SUBMERGEMENT_REAGENTS_TOUCH_AMOUNT 60

#define CHOKE_REAGENTS_INGEST_ON_FALL_AMOUNT 4

#define CHOKE_REAGENTS_INGEST_ON_BREATH_AMOUNT 2

#define SUBMERGEMENT_PERCENT(carbon, liquids) min(1,(carbon.body_position == LYING_DOWN ? liquids.liquid_state+LYING_DOWN_SUBMERGEMENT_STATE_BONUS : liquids.liquid_state)/TOTAL_LIQUID_STATES)

#define TOTAL_LIQUID_HEIGHT(turf) (turf.liquid_height + turf.liquids?.liquid_height)


code:
/// Max damage we consider for damage_organs()
#define MAX_CONSIDERED_ORGAN_DAMAGE_ROLL 75

// ~pulse levels, very simplified.
#define PULSE_NONE 0   // So !M.pulse checks would be possible.
#define PULSE_SLOW 1   // <60     bpm
#define PULSE_NORM 2   //  60-90  bpm
#define PULSE_FAST 3   //  90-120 bpm
#define PULSE_FASTER 4   // >120    bpm
#define PULSE_THREADY 5   // Occurs during hypovolemic shock
#define PULSE_MAX_BPM 250 // Highest, readable BPM by machines and humans.
#define GETPULSE_BASIC 0   // Less accurate. (hand, health analyzer, etc.)
#define GETPULSE_ADVANCED 1   // More accurate. (med scanner, sleeper, etc.)
#define GETPULSE_PERFECT 2   // Perfectly accurate. (currently no non-adminbus means, you get the exact value 0-5)

// ~germ defines
/// Medical equipment should start out as this
#define GERM_LEVEL_STERILE 0
/// Maximum germ level you can reach by standing still.
#define GERM_LEVEL_AMBIENT 250
/// Maximum germ level any atom can normally achieve
#define MAXIMUM_GERM_LEVEL 1000

/// Exposure to blood germ level per unit
#define GERM_PER_UNIT_BLOOD 2
/// Exposure to piss germ level per unit
#define GERM_PER_UNIT_PISS 2
/// Exposure to shit germ level per unit
#define GERM_PER_UNIT_SHIT 5

// ~sanitization defines, related to lowering germ level
/// Sterilizine sanitization per unit
#define SANITIZATION_PER_UNIT_STERILIZINE 50
/// Space cleaner sanitization per unit
#define SANITIZATION_PER_UNIT_SPACE_CLEANER 25
/// Water sanitization per unit
#define SANITIZATION_PER_UNIT_WATER 10
/// CE_ANTIBIOTIC bodypart/organ sanitization per CE unit
#define SANITIZATION_ANTIBIOTIC 0.1
/// Bodypart/organ sanitization for laying down
#define SANITIZATION_LYING 1

// ~infection levels
/// infections grow from ambient to one in ~5 minutes
#define INFECTION_LEVEL_ONE 250
/// infections grow from ambient to two in ~10 minutes
#define INFECTION_LEVEL_TWO 500
/// infections grow from two to three in ~15 minutes
#define INFECTION_LEVEL_THREE 1000

/// Maximum amount of germs surgery can cause
#define SURGERY_GERM_MAXIMUM 800

/// Organ and bodypart rejection levels
#define REJECTION_LEVEL_1 1
#define REJECTION_LEVEL_2 50
#define REJECTION_LEVEL_3 200
#define REJECTION_LEVEL_4 500

//~brain damage related defines
/// Brainloss caused by mildly low blood oxygenation
#define BRAIN_DAMAGE_LOW_OXYGENATION 1.5
/// Brainloss caused by lower than low blood oxygenation
#define BRAIN_DAMAGE_LOWER_OXYGENATION 3
/// Brainloss caused by a complete lack of oxygen flow
#define BRAIN_DAMAGE_LOWEST_OXYGENATION 4.5

/// We need to take at least this much brainloss gained at once to roll for brain traumas, any less it won't roll
#define TRAUMA_ROLL_THRESHOLD 4.5

//Brain damage defines
#undef BRAIN_DAMAGE_MILD
#undef BRAIN_DAMAGE_SEVERE
#undef BRAIN_DAMAGE_DEATH
#define BRAIN_DAMAGE_MILD 15
#define BRAIN_DAMAGE_SEVERE 75
#define BRAIN_DAMAGE_DEATH 200

// ~CPR types
/// Mouth to mouth - Heals oxygen deprivation
#define CPR_MOUTH "m2m"
#define CPR_CHEST "cardio"

/// Mouth to mouth cooldown duration
#define M2M_COOLDOWN 0.3 SECONDS
///Cpr cooldown duration
#define CPR_COOLDOWN 0.3 SECONDS

// ~simple brainloss defines
#define GETBRAINLOSS(mob) mob.getOrganLoss(ORGAN_SLOT_BRAIN)
#define ADJUSTBRAINLOSS(mob, amount) mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount)
#define SETBRAINLOSS(mob, amount) mob.setOrganLoss(ORGAN_SLOT_BRAIN, amount)


code:
// Since we don't use the tg burn wounds, we jack variables up a bit
// Remember however that this is damage per second, though (delta time)
#define HEAT_DAMAGE_LEVEL_1 1 //Amount of damage applied when your body temperature just passes the 360.15k safety point
#define HEAT_DAMAGE_LEVEL_2 2 //Amount of damage applied when your body temperature passes the 400K point
#define HEAT_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 460K point and you are on fire

#define COLD_DAMAGE_LEVEL_1 1 //Amount of damage applied when your body temperature just passes the 260.15k safety point
#define COLD_DAMAGE_LEVEL_2 2 //Amount of damage applied when your body temperature passes the 200K point
#define COLD_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 120K point

/// Mob status_flags
#define BLEEDOUT (1<<5)
#define BUILDING_ORGANS (1<<6)

/// Yeah we can't have 17 year olds. It's gonna get me exposed on reddit.
#define AGE_MIN 18 //youngest a character can be

/// Hydration levels for humans
#define HYDRATION_LEVEL_SOAKED 600
#define HYDRATION_LEVEL_FULL 550
#define HYDRATION_LEVEL_WELL_HYDRATED 450
#define HYDRATION_LEVEL_HYDRATED 350
#define HYDRATION_LEVEL_THIRSTY 250
#define HYDRATION_LEVEL_DEHYDRATED 150

#define HYDRATION_LEVEL_START_MIN 250
#define HYDRATION_LEVEL_START_MAX 400

/// Used as an upper limit for species that continuously gain hydration
#define HYDRATION_LEVEL_ALMOST_FULL 535

/// Arousal levels for humans
//maximum arousal one person can have
#define AROUSAL_LEVEL_MAXIMUM 1000
//second horny mood
#define AROUSAL_LEVEL_HORNY 550
//first horny mood
#define AROUSAL_LEVEL_AROUSED 300

#define AROUSAL_LEVEL_START_MIN 0
#define AROUSAL_LEVEL_START_MAX 200

/// Factor at which mob hydration decreases
#define THIRST_FACTOR 0.05

/// How much moving around increases carbon germ level
#define GERM_LEVEL_MOVEMENT_INCREASE 0.2

/// Germ levels for carbon mob hygiene
#define GERM_LEVEL_START_MIN 0
#define GERM_LEVEL_START_MAX 100
#define GERM_LEVEL_DIRTY 250
#define GERM_LEVEL_FILTHY 500
#define GERM_LEVEL_SMASHPLAYER 750

#undef CAN_SUCCUMB
#define CAN_SUCCUMB(target) (HAS_TRAIT(target, TRAIT_CRITICAL_CONDITION))


code:
#define CARDIAC_ARREST_SLOWDOWN 5
#define SHOCK_STAGE_SLOWDOWN 0.25
#define TRAUMATIC_SHOCK_SLOWDOWN_MULTIPLIER 0.03
#define LIMB_EFFICIENCY_MIN_SLOWDOWN -6
#define LIMB_EFFICIENCY_MAX_SLOWDOWN 6
#define SPRINT_DEFAULT_SPEED_INCREASE 1
#define SPRINT_DEFAULT_STAMINA_COST 4
#define SPRINT_MAX_STAMLOSS 50
#define SPRINT_MAX_FATIGUELOSS 50
#define SPEEDBOOST_SPEED_INCREASE 0.5


code:
#define DRONING_NEVADO list('modular_septic/sound/music/droning/nevado1.ogg', \
						'modular_septic/sound/music/droning/nevado2.ogg', \
						'modular_septic/sound/music/droning/nevado3.ogg', \
						'modular_septic/sound/music/droning/nevado4.ogg')

#define DRONING_BALUARTE list('modular_septic/sound/music/droning/baluarte1.ogg', \
						'modular_septic/sound/music/droning/baluarte2.ogg', \
						'modular_septic/sound/music/droning/baluarte3.ogg', \
						'modular_septic/sound/music/droning/baluarte4.ogg')

#define DRONING_AI list('modular_septic/sound/music/droning/aimonitored1.ogg', \
						'modular_septic/sound/music/droning/aimonitored2.ogg')

#define DRONING_SPACE list('modular_septic/sound/music/droning/space1.ogg', \
						'modular_septic/sound/music/droning/space2.ogg', \
						'modular_septic/sound/music/droning/space3.ogg')

#define DRONING_MAINT list('modular_septic/sound/music/droning/maint1.ogg', \
						'modular_septic/sound/music/droning/maint2.ogg')
#define DRONING_PITOFDESPAIR list('modular_septic/sound/music/droning/maint1.ogg')

#define DRONING_ENGI list('modular_septic/sound/music/droning/engineering1.ogg', \
						'modular_septic/sound/music/droning/engineering2.ogg')

#define DRONING_TAVERN list('modular_septic/sound/music/droning/tavern1.ogg', \
						'modular_septic/sound/music/droning/tavern2.ogg', \
						'modular_septic/sound/music/droning/tavern3.ogg', \
						'modular_septic/sound/music/droning/tavern4.ogg')

#define DRONING_SHUTTLE list('modular_septic/sound/music/droning/shuttle1.ogg')

#define DRONING_TRAIN list('modular_septic/sound/music/droning/train1.ogg')

#define DRONING_LIFT list('modular_septic/sound/music/droning/lift1.ogg')

#define DRONING_DEFAULT list('sound/ambience/shipambience.ogg')

#define DRONING_COMBAT list('modular_septic/sound/music/combat/ritual.ogg')


code:
// ~organ status var
///This organ is organic - It can get infected and heal damage over time
#define ORGAN_ORGANIC 1
///This organ is robotic - Affected by emps, doesn't get infected, doesn't heal damage over time
#define ORGAN_ROBOTIC 2

// ~organ organ_flags var
/// Frozen organ, doesn't rot
#define ORGAN_FROZEN (1<<0)
/// Failing organs don't work (duh) and might cause other ill effects
#define ORGAN_FAILING (1<<1)
/// Destroyed organs don't function and cannot be repaired, needs a transplant
#define ORGAN_DESTROYED (1<<2)
/// Not only is the organ failing, it is completely septic and spreading germs around
#define ORGAN_DEAD (1<<3)
/// Organ has been cut away from the owner and can be safely removed during surgery
#define ORGAN_CUT_AWAY (1<<4)
/// Currently only the brain - Removal of the organ will cause death to the owner
#define ORGAN_VITAL	(1<<5)
/// Organ is edible and uses the edible component
#define ORGAN_EDIBLE (1<<6)
/// Can be affected by EMPs - Organic or not
#define ORGAN_SYNTHETIC (1<<7)
/// Currently being affected by EMP effects
#define ORGAN_SYNTHETIC_EMP	(1<<8)
/// Does not rot nor get infected
#define ORGAN_NOINFECTION (1<<9)
/// Can't be removed via common means
#define ORGAN_UNREMOVABLE (1<<10)
/// Was this organ implanted/inserted/etc, if true will not be removed during species change
#define ORGAN_EXTERNAL (1<<11)
/// Organ cannot ever become destroyed beyond repair
#define ORGAN_INDESTRUCTIBLE (1<<12)
/// Organ should update limb efficiency when damaged or healed
#define ORGAN_LIMB_SUPPORTER (1<<13)
/// Organ shouldn't be counted in /obj/item/bodypart/proc/damage_internal_organs()
#define ORGAN_NO_VIOLENT_DAMAGE (1<<14)

/// How many toxins a single liver can hold
#define LIVER_MAX_TOXIN 50
/// How much toxin a single kidney can hold
#define KIDNEY_MAX_TOXIN 25

/// How much oxyloss a single lung can hold
#define LUNG_MAX_OXYLOSS 50

/// How much blood the spleen normally generates per second
#define BLOOD_REGEN_FACTOR	0.25

// ~kidney stuff
#define KIDNEY_DEFAULT_TOX_TOLERANCE 1.5 //amount of toxins a single kidney can filter out
#define KIDNEY_DEFAULT_TOX_LETHALITY 0.0025 //lower values lower how harmful toxins are to the kidneys

// ~toxin pain factors
#define LIVER_TOXIN_PAIN_FACTOR 1
#define KIDNEY_TOXIN_PAIN_FACTOR 0.75

//The contant in the rate of reagent transfer on life ticks
#define STOMACH_METABOLISM_CONSTANT 0.25

/// default organ maxHealth
#define STANDARD_ORGAN_THRESHOLD 60
/// designed to heal organs fully when left on a mob for ~4 minutes
#define STANDARD_ORGAN_HEALING 50 / 100000
/// designed to fail organs when left to decay for ~25 minutes
#define STANDARD_ORGAN_DECAY 100 / 100000

// ~should take around 20 minutes for a body to fully rot
#define MIN_ORGAN_DECAY_INFECTION 0.5
#define MAX_ORGAN_DECAY_INFECTION 1

/// Minimum organ damage at once to tell the player about the ouchie
#define ORGAN_DAMAGE_NOTIFY_PLAYER 4

// ~organ sides
#define NO_SIDE 0
#define RIGHT_SIDE (1<<0)
#define LEFT_SIDE (1<<1)

// ~organ slots
#define ORGAN_SLOT_BONE "bone"
#define ORGAN_SLOT_TENDON "tendon"
#define ORGAN_SLOT_NERVE "nerve"
#define ORGAN_SLOT_ARTERY "artery"
#define ORGAN_SLOT_ADAMANTINE_RESONATOR "adamantine resonator"
#define ORGAN_SLOT_APPENDIX "appendix"
#define ORGAN_SLOT_SPLEEN "spleen"
#define ORGAN_SLOT_BRAIN "brain"
#define ORGAN_SLOT_HEAD_AUG "head augment"
#define ORGAN_SLOT_BRAIN_ANTIDROP "brain antidrop"
#define ORGAN_SLOT_BRAIN_ANTISTUN "brain antistun"
#define ORGAN_SLOT_MOUTH_AUG "mouth device"
#define ORGAN_SLOT_BREATHING_TUBE "breathing tube"
#define ORGAN_SLOT_EARS "ear"
#define ORGAN_SLOT_EYES "eye"
#define ORGAN_SLOT_HEART "heart"
#define ORGAN_SLOT_HEART_AID "heart aid"
#define ORGAN_SLOT_HUD "eye hud"
#define ORGAN_SLOT_LIVER "liver"
#define ORGAN_SLOT_KIDNEYS "kidney"
#define ORGAN_SLOT_LUNGS "lung"
#define ORGAN_SLOT_PARASITE_EGG "parasite egg"
#define ORGAN_SLOT_REGENERATIVE_CORE "hivecore"
#define ORGAN_SLOT_ARM_AUG "arm augment"
#define ORGAN_SLOT_RIGHT_ARM_AUG "right arm augment"
#define ORGAN_SLOT_LEFT_ARM_AUG "left arm augment" //This one ignores alphabetical order cause the arms should be together
#define ORGAN_SLOT_STOMACH "stomach"
#define ORGAN_SLOT_STOMACH_AID "stomach aid"
#define ORGAN_SLOT_INTESTINES "intestines"
#define ORGAN_SLOT_BLADDER "bladder"
#define ORGAN_SLOT_CHEST_AUG "chest augment"
#define ORGAN_SLOT_TAIL "tail"
#define ORGAN_SLOT_THRUSTERS "thrusters"
#define ORGAN_SLOT_TONGUE "tongue"
#define ORGAN_SLOT_VOICE "vocal cord"
#define ORGAN_SLOT_ZOMBIE "zombie infection"

// ~genital slots
#define ORGAN_SLOT_PENIS "penis"
#define ORGAN_SLOT_TESTICLES "testicles"
#define ORGAN_SLOT_VAGINA "vagina"
#define ORGAN_SLOT_WOMB "womb"
#define ORGAN_SLOT_BREASTS "breasts"

// ~xenomorph organ slots
#define ORGAN_SLOT_XENO_ACIDGLAND "acid_gland"
#define ORGAN_SLOT_XENO_EGGSAC "eggsac"
#define ORGAN_SLOT_XENO_HIVENODE "hive_node"
#define ORGAN_SLOT_XENO_NEUROTOXINGLAND "neurotoxin_gland"
#define ORGAN_SLOT_XENO_PLASMAVESSEL "plasma_vessel"
#define ORGAN_SLOT_XENO_RESINSPINNER "resin_spinner"

// ~organ slot external
#define ORGAN_SLOT_EXTERNAL_TAIL "tail"
#define ORGAN_SLOT_EXTERNAL_SPINES "spines"
#define ORGAN_SLOT_EXTERNAL_SNOUT "snout"
#define ORGAN_SLOT_EXTERNAL_FRILLS "frills"
#define ORGAN_SLOT_EXTERNAL_HORNS "horns"
#define ORGAN_SLOT_EXTERNAL_WINGS "wings"
#define ORGAN_SLOT_EXTERNAL_ANTENNAE "antennae"
#define ORGAN_SLOT_EXTERNAL_BODYMARKINGS "bodymarkings"

// ~bones
#define BONE_MAX_HEALTH 100

#define BONE_HEAD /obj/item/organ/bone/head
#define BONE_MOUTH /obj/item/organ/bone/mouth
#define BONE_NECK /obj/item/organ/bone/neck
#define BONE_CHEST /obj/item/organ/bone/chest
#define BONE_GROIN /obj/item/organ/bone/groin
#define BONE_L_ARM /obj/item/organ/bone/l_arm
#define BONE_L_HAND	/obj/item/organ/bone/l_hand
#define BONE_R_ARM /obj/item/organ/bone/r_arm
#define BONE_R_HAND /obj/item/organ/bone/r_hand
#define BONE_L_LEG /obj/item/organ/bone/l_leg
#define BONE_L_FOOT /obj/item/organ/bone/l_foot
#define BONE_R_LEG /obj/item/organ/bone/r_leg
#define BONE_R_FOOT /obj/item/organ/bone/r_foot

// ~bone flags
/// can suffer dislocations
#define BONE_JOINTED (1<<0)
/// encases the limb, providing protection to internal organs
#define BONE_ENCASING (1<<1)

// ~tendons
#define TENDON_MAX_HEALTH 100

#define TENDON_R_EYE /obj/item/organ/tendon/r_eye
#define TENDON_L_EYE /obj/item/organ/tendon/l_eye
#define TENDON_HEAD /obj/item/organ/tendon/head
#define TENDON_MOUTH /obj/item/organ/tendon/mouth
#define TENDON_NECK /obj/item/organ/tendon/neck
#define TENDON_CHEST /obj/item/organ/tendon/chest
#define TENDON_GROIN /obj/item/organ/tendon/groin
#define TENDON_L_ARM /obj/item/organ/tendon/l_arm
#define TENDON_L_HAND /obj/item/organ/tendon/l_hand
#define TENDON_R_ARM /obj/item/organ/tendon/r_arm
#define TENDON_R_HAND /obj/item/organ/tendon/r_hand
#define TENDON_L_LEG /obj/item/organ/tendon/l_leg
#define TENDON_L_FOOT /obj/item/organ/tendon/l_foot
#define TENDON_R_LEG /obj/item/organ/tendon/r_leg
#define TENDON_R_FOOT /obj/item/organ/tendon/r_foot

// ~nerves
#define NERVE_MAX_HEALTH 100

#define NERVE_L_EYE /obj/item/organ/nerve/l_eye
#define NERVE_R_EYE /obj/item/organ/nerve/r_eye
#define NERVE_HEAD /obj/item/organ/nerve/head
#define NERVE_MOUTH /obj/item/organ/nerve/mouth
#define NERVE_NECK /obj/item/organ/nerve/neck
#define NERVE_CHEST /obj/item/organ/nerve/chest
#define NERVE_GROIN	/obj/item/organ/nerve/groin
#define NERVE_L_ARM /obj/item/organ/nerve/l_arm
#define NERVE_L_HAND /obj/item/organ/nerve/l_hand
#define NERVE_R_ARM	/obj/item/organ/nerve/r_arm
#define NERVE_R_HAND /obj/item/organ/nerve/r_hand
#define NERVE_L_LEG	/obj/item/organ/nerve/l_leg
#define NERVE_L_FOOT /obj/item/organ/nerve/l_foot
#define NERVE_R_LEG /obj/item/organ/nerve/r_leg
#define NERVE_R_FOOT /obj/item/organ/nerve/r_foot

// ~arteries
#define ARTERY_MAX_HEALTH 100
#define ARTERIAL_BLOOD_FLOW 3

#define ARTERY_L_EYE /obj/item/organ/artery/l_eye
#define ARTERY_R_EYE /obj/item/organ/artery/r_eye
#define ARTERY_HEAD /obj/item/organ/artery/head
#define ARTERY_MOUTH /obj/item/organ/artery/mouth
#define ARTERY_NECK /obj/item/organ/artery/neck
#define ARTERY_CHEST /obj/item/organ/artery/chest
#define ARTERY_GROIN /obj/item/organ/artery/groin
#define ARTERY_L_ARM /obj/item/organ/artery/l_arm
#define ARTERY_L_HAND /obj/item/organ/artery/l_hand
#define ARTERY_R_ARM /obj/item/organ/artery/r_arm
#define ARTERY_R_HAND /obj/item/organ/artery/r_hand
#define ARTERY_L_LEG /obj/item/organ/artery/l_leg
#define ARTERY_L_FOOT /obj/item/organ/artery/l_foot
#define ARTERY_R_LEG /obj/item/organ/artery/r_leg
#define ARTERY_R_FOOT /obj/item/organ/artery/r_foot

// ~efficiency defines
#define ORGAN_OPTIMAL_EFFICIENCY 100
#define ORGAN_BRUISED_EFFICIENCY 80
#define ORGAN_FAILING_EFFICIENCY 50
#define ORGAN_DESTROYED_EFFICIENCY 0

// ~organ requirements
/// Normally 50% of the default blood volume (230cl)
#define DEFAULT_TOTAL_BLOOD_REQ	BLOOD_VOLUME_NORMAL * 0.5
#define DEFAULT_TOTAL_OXYGEN_REQ 50
#define DEFAULT_TOTAL_NUTRIMENT_REQ	HUNGER_FACTOR
#define DEFAULT_TOTAL_HYDRATION_REQ THIRST_FACTOR

// ~simple list of organs that come in pairs
#define PAIRED_ORGAN_SLOTS list(ORGAN_SLOT_LUNGS, \
							ORGAN_SLOT_KIDNEYS, \
							ORGAN_SLOT_EYES, \
							ORGAN_SLOT_EARS)


code:
// ~pain levels when using the custom_pain proc and shit
#define PAIN_EMOTE_MINIMUM 10
#define PAIN_LEVEL_1 0
#define PAIN_LEVEL_2 10
#define PAIN_LEVEL_3 40
#define PAIN_LEVEL_4 70

// ~shock stages
#define SHOCK_STAGE_1 10
#define SHOCK_STAGE_2 30
#define SHOCK_STAGE_3 40
#define SHOCK_STAGE_4 60 // "Softcrit"
#define SHOCK_STAGE_5 80
#define SHOCK_STAGE_6 120 // "Hardcrit"
#define SHOCK_STAGE_7 150
#define SHOCK_STAGE_8 200
#define SHOCK_STAGE_MAX SHOCK_STAGE_8

// ~shock modifiers
#define SHOCK_MOD_BRUTE 0.7
#define SHOCK_MOD_BURN 0.8
#define SHOCK_MOD_TOXIN 1
#define SHOCK_MOD_CLONE 1.1

/// Above or equal this pain, we give in
#define PAIN_GIVES_IN 75
/// Above or equal to this amount of pain, we can only speak in whispers
#define PAIN_NO_SPEAK 120
/// Above or equal this pain, we cannot sleep intentionally
#define PAIN_NO_SLEEP 50


code:
#define REGISTER_POLLUTION(pollution) SSpollution.all_polution[pollution] = TRUE
#define UNREGISTER_POLLUTION(pollution) SSpollution.all_polution -= pollution
#define SET_ACTIVE_POLLUTION(pollution) SSpollution.active_pollution[pollution] = TRUE
#define SET_UNACTIVE_POLLUTION(pollution) SSpollution.active_pollution -= pollution
#define SET_PROCESSED_THIS_RUN(pollution) SSpollution.processed_this_run[pollution] = TRUE
#define REMOVE_POLLUTION_CURRENTRUN(pollution) SSpollution.current_run -= pollution

#define POLLUTION_HEIGHT_DIVISOR 10

#define TICKS_TO_DISSIPATE 20

#define POLLUTION_TASK_PROCESS 1
#define POLLUTION_TASK_DISSIPATE 2

#define SCENT_DESC_ODOR "odour"
#define SCENT_DESC_SMELL "smell"
#define SCENT_DESC_FRAGRANCE "fragrance"

///Minimum amount of smell power to be able to sniff a pollutant
#define POLLUTANT_SMELL_THRESHOLD 3.5

#define POLLUTANT_SMELL_NORMAL 20
#define POLLUTANT_SMELL_STRONG 40

#define SMELL_COOLDOWN 1 MINUTES

//Bitflags for pollutants
#define POLLUTANT_APPEARANCE (1<<0) //Pollutant has an appearance
#define POLLUTANT_SMELL (1<<1) //Pollutant has a smell
#define POLLUTANT_TOUCH_ACT (1<<2) //Pollutant calls TouchAct() on unprotected people touched by it
#define POLLUTANT_BREATHE_ACT (1<<3) //Pollutant calls SmellAct() on people breathing it in

#define POLLUTANT_APPEARANCE_THICKNESS_THRESHOLD 30
#define THICKNESS_ALPHA_COEFFICIENT 0.0025

//Cap for active emitters that can be running for a very long time
#define POLLUTION_ACTIVE_EMITTER_CAP 300


code:
#define TOGGLE_FULLSCREEN (1<<0)

#define SEPTICTOGGLES_DEFAULT (TOGGLE_FULLSCREEN)

#define MAX_FLAVOR_LEN 4096

// This is used both to buy augments and to buy languages in the setup
#define MAXIMUM_CUSTOMIZATION_POINTS 4

#define LANGUAGE_COST_SPOKEN 2
#define LANGUAGE_COST_UNDERSTOOD 1

//randomised elements
#define RANDOM_FEATURES "random_features"

// Values for /datum/preferences/current_tab
/// Open the character preference window
#undef PREFERENCE_TAB_CHARACTER_PREFERENCES
#define PREFERENCE_TAB_CHARACTER_PREFERENCES 0


code:
//Messages to send the buried person
#define DEFAULT_REST_TARGET_MSG "My body rots away, disintegrating into putrid organic matter and ashes. As my ethereal form fades into the heavens, my past life and sins are forgotten as I sever the link from my body."
#define CREMATE_REST_TARGET_MSG "As the roaring fires consume my hollow corpse, the chains holding my soul to this plane loosen up.. <b>I can finally rest.</b>"

//Messages to send the observers
#define DEFAULT_REST_OBSERVER_MSG(x) "As the last pile of dirt covers [x]'s body, maggots and worms crawl above the surface. It's soothing to know the cycle of life and death is now properly fulfilled."
#define CREMATE_REST_OBSERVER_MSG(x) "The roaring flames consume [x] rotting carcass. The gasses that come out are putrid and smell of char and rot, but it is soothing to know that [x] can rest."


code: #define PUNCTUATION_FILTER_CHECK(T) (config.punctuation_filter_regex && !findtext(T, config.punctuation_filter_regex, length(message)) && !findtext(T, config.punctuation_filter_bypass_regex, 1, 2))


code:
// Describes the three modes of scanning available for health analyzers
#define SCANMODE_HEALTH 0
#define SCANMODE_WOUND 1
#define SCANMODE_COUNT 2 // Update this to be the number of scan modes if you add more
#define SCANNER_CONDENSED 0
#define SCANNER_VERBOSE 1


code:
#define SCREENTIP_OPENSPACE(name) "[name]"
#define SCREENTIP_TURF(name) "[name]"
#define SCREENTIP_OBJ(name) "[name]"
#define SCREENTIP_MOB(name) "[name]"

// ~screentip flags
#define SCREENTIP_ON_MOUSE_ENTERED (1<<0)
#define SCREENTIP_ON_MOUSE_MOVE (1<<1)
#define SCREENTIP_ON_MOUSE_CLICK (1<<2)

#define DEFAULT_SCREENTIP_FLAGS (SCREENTIP_ON_MOUSE_ENTERED)
#define SCREENTIP_HOVERER (SCREENTIP_ON_MOUSE_ENTERED|SCREENTIP_ON_MOUSE_MOVE)
#define SCREENTIP_HOVERER_CLICKER (SCREENTIP_ON_MOUSE_ENTERED|SCREENTIP_ON_MOUSE_MOVE|SCREENTIP_ON_MOUSE_CLICK)
#define SCREENTIP_CLICKER (SCREENTIP_ON_MOUSE_ENTERED|SCREENTIP_ON_MOUSE_CLICK)


code:
#define CHANNEL_CLICKITY_CLACK 1016
#define CHANNEL_EAR_RING 1015
#define CHANNEL_RADIO_CHATTER 1014


code:
#define div_infobox(str) ("
" + str + "
")
#define span_bling(str) ("" + str + "")
#define span_userlove(str) ("" + str + "")
#define span_love(str) ("" + str + "")
#define span_horny(str) ("" + str + "")
#define span_animatedpain(str) ("" + str + "")
#define span_lowpain(str) ("" + str + "")
#define span_lowestpain(str) ("" + str + "")
#define span_artery(str) ("" + str + "")
#define span_infection(str) ("" + str + "")
#define span_necrosis(str) ("" + str + "")
#define span_dead(str) ("" + str + "")
#define span_flashinguserdanger(str) ("" + str + "")
#define span_flashingdanger(str) ("" + str + "")
#define span_bolddanger(str) ("" + str + "")
#define span_emote(str) ("" + str + "")


code:
/// The affected is unable to use or pickup items
#define STATUS_EFFECT_DAZED /datum/status_effect/incapacitating/dazed
/// The affected is unable to use or pickup items, plus will fall down depending on stats
#define STATUS_EFFECT_STUMBLE /datum/status_effect/incapacitating/dazed/stumble
/// The affected mob gets a very annoying screen effect
#define STATUS_EFFECT_HEADRAPE /datum/status_effect/incapacitating/headrape


code:
/// Must be in the user's hands to be accessed
#define STORAGE_NO_WORN_ACCESS (1<<0)
/// Must be out of the user to be accessed
#define STORAGE_NO_EQUIPPED_ACCESS (1<<1)


code:
#define INIT_ORDER_WHO 84
#define INIT_ORDER_POLLUTION 32 //Needs to be above atoms


code:
// ~return values for the how_open() proc
#define SURGERY_INCISED (1<<0)
#define SURGERY_RETRACTED (1<<1)
#define SURGERY_DISLOCATED (1<<2)
#define SURGERY_BROKEN (1<<3)
#define SURGERY_DRILLED (1<<4)

// ~flags for requirements for a surgery step
#define STEP_NEEDS_INCISED (1<<0)
#define STEP_NEEDS_NOT_INCISED (1<<1)
#define STEP_NEEDS_RETRACTED (1<<2)
#define STEP_NEEDS_DISLOCATED (1<<3)
#define STEP_NEEDS_BROKEN (1<<4)
#define STEP_NEEDS_DRILLED (1<<5)
#define STEP_NEEDS_ENCASED (1<<6)

#define SURGERY_ADVANCE 1
#define SURGERY_DONT_ADVANCE 0
#define SURGERY_CANCEL -1


code: #define YEAR_OFFSET 657


code:
// ~MOB TRAITS
/// Does not feel pain
#define TRAIT_NOPAIN "no_pain"
/// Pain gives a mood buff instead of debuff
#define TRAIT_PAINLOVER "pain_good"
/// Does not need hydration
#define TRAIT_NOTHIRST "no_thirst"
/// Does not need to shit
#define TRAIT_NODEFECATION "no_defecation"
/// Does not need to piss
#define TRAIT_NOURINATION "no_urination"
/// Intentionally sleeping
#define TRAIT_TRYINGTOSLEEP "tryna_sleep"
///Can bleed, but not affected by blood loss
#define TRAIT_BLOODLOSSIMMUNE "blood_immune"
///Cannot sprint, but can have sprint active
#define TRAIT_SPRINT_LOCKED	"sprint_locked"
///Cannot be husked via common means
#define TRAIT_NO_HUSK "no_husk"
///Actively hiding furry bodyparts
#define TRAIT_HIDING_MUTANTPARTS "hiding_mutantparts"
///Active typing indicator
#define TRAIT_TYPINGINDICATOR "typing_indicator"
///Active SSD indicator
#define TRAIT_SSDINDICATOR "ssd_indicator"
///Stumbling, can smash into things
#define TRAIT_STUMBLE "stumbling"
///Basically the same as being strangled
#define TRAIT_HOLDING_BREATH "holding_breath"
///Can breathe underwater
#define TRAIT_WATER_BREATHING "water_breathing"
///Can't parry
#define TRAIT_NO_PARRY "no_parry"
///Fraggot system
#define TRAIT_FRAGGOT "fraggot"
///Fluoride stare
#define TRAIT_FLUORIDE_STARE "fluoride_stare"

// ~BODYPART TRAITS
///Rotten beyond salvation
#define TRAIT_ROTTEN "rotten"
///Genetically deformed beyond salvation
#define TRAIT_DEFORMED "deformed"

// ~MIND TRAITS
/// Mood will say "This is great for the economy" and other funny shit
#define TRAIT_CAPITALIST_MOOD "capitalist_mood"
/// Cannot be laid to rest via burial or cremation
#define TRAIT_NO_LAYTOREST "no_laytorest"

// ~TRAIT SOURCES
#define GERM_LEVEL "germ_level"
#define GRAB_TRAIT "grab"
#define COMMUNICATION_TRAIT "communication"
#define CLINGING_TRAIT "clinging"
#define VERB_TRAIT "verb"


code:
#define ENCUMBRANCE_NONE 0
#define ENCUMBRANCE_LIGHT 1
#define ENCUMBRANCE_MEDIUM 2
#define ENCUMBRANCE_HEAVY 3
#define ENCUMBRANCE_EXTREME 4


code:
// ~wield flags
/// Can be wielded
#define WIELD_WIELDABLE (1<<0)
/// Requires two hands to be equipped
#define WIELD_ALWAYS_TWOHANDED (1<<1)
/// Has no attack self wielding
#define WIELD_NO_ATTACK_SELF (1<<2)


code:
// ~wound damage/rolling defines
/// the cornerstone of the wound threshold system, your base wound roll for any attack is rand(1, damage^this), after armor reduces said damage. See [/obj/item/bodypart/proc/check_wounding]
#define WOUND_DAMAGE_EXPONENT 1.4
/// any damage dealt over this is ignored for damage rolls unless the target has the frail quirk (50^1.4=250)
#define WOUND_MAX_CONSIDERED_DAMAGE 50
/// an attack must do this much damage after armor in order to roll for being a wound (so pressure damage/being on fire doesn't proc it)
#define WOUND_MINIMUM_DAMAGE 5
/// an attack must do this much damage after armor in order to be eliigible to dismember a suitably mushed bodypart
#define DISMEMBER_MINIMUM_DAMAGE 7.5
/// If an attack rolls this high with their wound (including mods), we try to outright dismember the limb. Note 250 is incredibly hard to reach even with the maximum damage! (1/250 chance)
#define WOUND_DISMEMBER_OUTRIGHT_THRESH 250
/// set wound_bonus on an item or attack to this to disable checking wounding for the attack
#define CANT_WOUND -100

/// an attack must do this much damage after armor to roll for artery wounds
#define ARTERY_MINIMUM_DAMAGE 10
/// ditto but for tendon wounds
#define TENDON_MINIMUM_DAMAGE 10
/// ditto but for nerve wounds
#define NERVE_MINIMUM_DAMAGE 15
/// ditto but for internal organ damage
#define ORGAN_MINIMUM_DAMAGE 12.5
/// ditto but for teeth
#define TEETH_MINIMUM_DAMAGE 5

// ~wound severities
/// for jokey/meme wounds like stubbed toe, no standard messages/sounds or second winds
#define WOUND_SEVERITY_TRIVIAL 0
#define WOUND_SEVERITY_MODERATE 1
#define WOUND_SEVERITY_SEVERE 2
#define WOUND_SEVERITY_CRITICAL 3
/// outright dismemberment of limb
#define WOUND_SEVERITY_LOSS 4

// ~wound categories
/// doesn't actually wound
#define WOUND_NONE 0
/// any brute weapon/attack that doesn't have sharpness. rolls for blunt bone wounds
#define WOUND_BLUNT 1
/// any sharp weapon, edged or pointy, can cause arteries to be torn
#define WOUND_ARTERY 2
/// any sharp weapon, edged or pointy, can cause tendons to be torn
#define WOUND_TENDON 3
/// any sharp weapon, edged or pointy, can cause nerves to be torn
#define WOUND_NERVE 4
/// any brute weapon/attack with sharpness = SHARP_EDGED. rolls for slash wounds
#define WOUND_SLASH 5
/// any brute weapon/attack with sharpness = SHARP_POINTY. rolls for piercing wounds
#define WOUND_PIERCE 6
/// any concentrated burn attack (lasers really). rolls for burning wounds
#define WOUND_BURN 7
/// britification lol
#define WOUND_TEETH 8

// ~determination second wind defines
// How much determination reagent to add each time someone gains a new wound in [/datum/wound/proc/second_wind]
#define WOUND_DETERMINATION_MODERATE 1
#define WOUND_DETERMINATION_SEVERE 2.5
#define WOUND_DETERMINATION_CRITICAL 5
#define WOUND_DETERMINATION_LOSS 7.5

/// the max amount of determination you can have
#define WOUND_DETERMINATION_MAX 10

/// While someone has determination in their system, their bleed rate is slightly reduced
#define WOUND_DETERMINATION_BLEED_MOD 0.85

// ~infection defines
// Thresholds for infection for injuries, limbs and organs - once germ_level hits each threshold, things get steadily worse
/// below this has no ill effects from infection
#define WOUND_INFECTION_MODERATE 250
/// then below here, you ooze some pus and suffer minor tox damage, but nothing serious
#define WOUND_INFECTION_SEVERE 330
/// then below here, your limb occasionally locks up from damage and infection and briefly becomes disabled. Things are getting really bad
#define WOUND_INFECTION_CRITICAL 600
/// below here, your skin is almost entirely falling off and your limb locks up more frequently. You are within a stone's throw of septic paralysis and losing the limb
#define WOUND_INFECTION_SEPTIC 1000
// above WOUND_INFECTION_SEPTIC, your limb is completely putrid and you start rolling to lose the entire limb by way of paralyzation. After 3 failed rolls (~4-5% each probably), the limb is paralyzed

// ~wound global lists
// list in order of highest severity to lowest
GLOBAL_LIST_INIT(global_wound_types, list(
		WOUND_BLUNT = list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate),
		WOUND_ARTERY = list(/datum/wound/artery),
		WOUND_TENDON = list(/datum/wound/tendon),
		WOUND_NERVE = list(/datum/wound/nerve),
		WOUND_TEETH = list(/datum/wound/teeth)
		))

// every single type of wound that can be rolled naturally, in case you need to pull a random one
GLOBAL_LIST_INIT(global_all_wound_types, \
	list(/datum/wound/blunt/critical, \
	/datum/wound/blunt/severe, \
	/datum/wound/blunt/moderate, \
	/datum/wound/artery, \
	/datum/wound/tendon, \
	/datum/wound/nerve, \
	/datum/wound/teeth))

// ~random wound balance defines
/// how quickly sanitization removes infestation and decays per second
#define WOUND_INFECTION_SANITIZATION_RATE 10
/// sanitization for each point in the antibiotic chem effect
#define WOUND_SANITIZATION_PER_ANTIBIOTIC 1
/// how much sterilizer sanitizes a wound
#define WOUND_SANITIZATION_STERILIZER 100
/// how much we seep gauze per life tick
#define WOUND_INFECTION_SEEP_RATE 0.15
/// how much blood you can lose per second per slash max. 4 is a LOT of blood for one cut so don't worry about hitting it easily
#define WOUND_SLASH_MAX_BLOODFLOW 4
/// dead people don't bleed, but they can clot! this is the minimum amount of clotting per tick on dead people, so even critical cuts will slowly clot in dead people
#define WOUND_SLASH_DEAD_CLOT_MIN 0.025
/// if we suffer a bone wound to the head that creates brain traumas, the timer for the trauma cycle is +/- by this percent (0-100)
#define WOUND_BONE_HEAD_TIME_VARIANCE 20

// ~mangling defines
// With the wounds pt. 2 update, general dismemberment now requires 2 things for a limb to be dismemberable (bone only creatures just need the second):
// 1. Skin is mangled: A critical slash or pierce wound on that limb
// 2. Bone is mangled: At least a severe bone wound on that limb
// see [/obj/item/bodypart/proc/get_mangled_state] for more information
#define BODYPART_MANGLED_NONE 0
#define BODYPART_MANGLED_BONE 1
#define BODYPART_MANGLED_FLESH 2
#define BODYPART_MANGLED_BOTH 3

// ~biology defines
// What kind of biology we have, and what wounds we can suffer, mostly relies on the HAS_FLESH and HAS_BONE species traits on human species
/// golems and androids, cannot suffer any wounds
#define BIO_INORGANIC 0
/// skeletons and plasmemes, can only suffer bone wounds, only needs mangled bone to be able to dismember
#define BIO_JUST_BONE 1
/// nothing right now, maybe slimepeople in the future, can only suffer slashing, piercing, and burn wounds
#define BIO_JUST_FLESH 2
/// standard humanoids, can suffer all wounds, needs mangled bone and flesh to dismember. conveniently, what you get when you combine BIO_JUST_BONE and BIO_JUST_FLESH
#define BIO_FLESH_BONE 3

// ~wound flag defines
/// This wound creates sounds hints when applied
#define WOUND_SOUND_HINTS (1<<0)
/// The wound gets treated before others
#define WOUND_PRIORITY (1<<1)
/// If having this wound counts as mangled flesh for dismemberment
#define WOUND_MANGLES_FLESH (1<<3)
/// If having this wound counts as mangled bone for dismemberment
#define WOUND_MANGLES_BONE (1<<4)
/// This wound is visible even under clothing
#define WOUND_VISIBLE_THROUGH_CLOTHING (1<<5)
/// This wound does an injection check before being treated
#define WOUND_INJECT_CHECK (1<<6)
/// Stumps can be affected by this wound
#define WOUND_ACCEPTS_STUMP (1<<7)
/// The wound can stack with others of the same type
#define WOUND_STACKABLE (1<<8)

// ~injury flags
/// This injury creates sounds hints when applied
#define INJURY_SOUND_HINTS (1<<0)
/// This injury is bandaged and won't bleed
#define INJURY_BANDAGED (1<<1)
/// This injury is sutured and won't bleed
#define INJURY_SUTURED (1<<2)
/// This injury is clamped and won't bleed
#define INJURY_CLAMPED (1<<3)
/// This injury is salved, and the infection won't progress
#define INJURY_SALVED (1<<4)
/// This injury is disinfected, and the infection has been wiped AND won't progress
#define INJURY_DISINFECTED (1<<5)
/// This is a surgical injury and will not autoheal
#define INJURY_SURGICAL (1<<6)
/// This injury is retracted and gives access to people's yummy guts and bones
#define INJURY_RETRACTED (1<<7)
/// This injury has been drilled and will let you put stuff in a cavity (dental implants and cavity implants)
#define INJURY_DRILLED (1<<8)
/// The bones have been set in this injury and are waiting to be gelled
#define INJURY_SET_BONES (1<<9)

// ~scar persistence defines
// The following are the order placements for persistent scar save formats
/// The version number of the scar we're saving, any scars being loaded below this number will be discarded, see SCAR_CURRENT_VERSION below
#define SCAR_SAVE_VERS 1
/// The body_zone we're applying to on granting
#define SCAR_SAVE_ZONE 2
/// The description we're loading
#define SCAR_SAVE_DESC 3
/// The precise location we're loading
#define SCAR_SAVE_PRECISE_LOCATION 4
/// The severity the scar had
#define SCAR_SAVE_SEVERITY 5
/// Whether this is a BIO_JUST_BONE scar, a BIO_JUST_FLESH scar, or a BIO_FLESH_BONE scar (so you can't load fleshy human scars on a plasmaman character)
#define SCAR_SAVE_BIOLOGY 6
/// Which character slot this was saved to
#define SCAR_SAVE_CHAR_SLOT 7
///how many fields we save for each scar (so the number of above fields)
#define SCAR_SAVE_LENGTH 7

/// saved scars with a version lower than this will be discarded, increment when you update the persistent scarring format in a way that invalidates previous saved scars (new fields, reordering, etc)
#define SCAR_CURRENT_VERSION 3
/// how many scar slots, per character slot, we have to cycle through for persistent scarring, if enabled in character prefs
#define PERSISTENT_SCAR_SLOTS 3

// ~blood_flow rates of change, these are used by [/datum/wound/proc/get_bleed_rate_of_change] from [/mob/living/carbon/proc/bleed_warn] to let the player know if their bleeding is getting better/worse/the same
/// Our wound is clotting and will eventually stop bleeding if this continues
#define BLOOD_FLOW_DECREASING -1
/// Our wound is bleeding but is holding steady at the same rate.
#define BLOOD_FLOW_STEADY 0
/// Our wound is bleeding and actively getting worse, like if we're a critical slash or if we're afflicted with heparin
#define BLOOD_FLOW_INCREASING 1

/// How often can we annoy the player about their bleeding? This duration is extended if it's not serious bleeding
#define BLEEDING_MESSAGE_BASE_CD 15 SECONDS


code:
/*
 * Holds procs to help with list operations
 * Contains groups:
 * Misc
 * Sorting
 */

/*
 * Misc
 */

///Initialize the lazylist
#define LAZYINITLIST(L) if (!L) { L = list(); }
///If the provided list is empty, set it to null
#define UNSETEMPTY(L) if (L && !length(L)) L = null
///Like LAZYCOPY - copies an input list if the list has entries, If it doesn't the assigned list is nulled
#define LAZYLISTDUPLICATE(L) (L ? L.Copy() : null )
///Remove an item from the list, set the list to null if empty
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
///Add an item to the list, if the list is null it will initialize it
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
///Add an item to the list if not already present, if the list is null it will initialize it
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
///Returns the key of the submitted item in the list
#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
///returns L[I] if L exists and I is a valid index of L, runtimes if L is not a list
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
///Sets the item K to the value V, if the list is null it will initialize it
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
///Returns the lenght of the list
#define LAZYLEN(L) length(L)
///Sets a list to null
#define LAZYNULL(L) L = null
///Adds to the item K the value V, if the list is null it will initialize it
#define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += V;
///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
#define LAZYADDASSOCLIST(L, K, V) if(!L) { L = list(); } L[K] += list(V);
///Removes the value V from the item K, if the item K is empty will remove it from the list, if the list is empty will set the list to null
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }
///Accesses an associative list, returns null if nothing is found
#define LAZYACCESSASSOC(L, I, K) L ? L[I] ? L[I][K] ? L[I][K] : null : null : null
///Qdel every item in the list before setting the list to null
#define QDEL_LAZYLIST(L) for(var/I in L) qdel(I); L = null;
//These methods don't null the list
///Use LAZYLISTDUPLICATE instead if you want it to null with no entries
#define LAZYCOPY(L) (L ? L.Copy() : list() )
/// Consider LAZYNULL instead
#define LAZYCLEARLIST(L) if(L) L.Cut()
///Returns the list if it's actually a valid list, otherwise will initialize it
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
#define reverseList(L) reverse_range(L.Copy())

/// Performs an insertion on the given lazy list with the given key and value. If the value already exists, a new one will not be made.
#define LAZYORASSOCLIST(lazy_list, key, value) \
	LAZYINITLIST(lazy_list); \
	LAZYINITLIST(lazy_list[key]); \
	lazy_list[key] |= value;

/// Passed into BINARY_INSERT to compare keys
#define COMPARE_KEY __BIN_LIST[__BIN_MID]
/// Passed into BINARY_INSERT to compare values
#define COMPARE_VALUE __BIN_LIST[__BIN_LIST[__BIN_MID]]

/****
	* Binary search sorted insert
	* INPUT: Object to be inserted
	* LIST: List to insert object into
	* TYPECONT: The typepath of the contents of the list
	* COMPARE: The object to compare against, usualy the same as INPUT
	* COMPARISON: The variable on the objects to compare
	* COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE.
	*/
#define BINARY_INSERT(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \
	do {\
		var/list/__BIN_LIST = LIST;\
		var/__BIN_CTTL = length(__BIN_LIST);\
		if(!__BIN_CTTL) {\
			__BIN_LIST += INPUT;\
		} 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 = COMPTYPE;\
				if(__BIN_ITEM.##COMPARISON <= COMPARE.##COMPARISON) {\
					__BIN_LEFT = __BIN_MID + 1;\
				} else {\
					__BIN_RIGHT = __BIN_MID;\
				};\
				__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
			};\
			__BIN_ITEM = COMPTYPE;\
			__BIN_MID = __BIN_ITEM.##COMPARISON > COMPARE.##COMPARISON ? __BIN_MID : __BIN_MID + 1;\
			__BIN_LIST.Insert(__BIN_MID, INPUT);\
		};\
	} while(FALSE)

/**
 * Custom binary search sorted insert utilising comparison procs instead of vars.
 * INPUT: Object to be inserted
 * LIST: List to insert object into
 * TYPECONT: The typepath of the contents of the list
 * COMPARE: The object to compare against, usualy the same as INPUT
 * COMPARISON: The plaintext name of a proc on INPUT that takes a single argument to accept a single element from LIST and returns a positive, negative or zero number to perform a comparison.
 * COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE.
 */
#define BINARY_INSERT_PROC_COMPARE(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \
	do {\
		var/list/__BIN_LIST = LIST;\
		var/__BIN_CTTL = length(__BIN_LIST);\
		if(!__BIN_CTTL) {\
			__BIN_LIST += INPUT;\
		} 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 = COMPTYPE;\
				if(__BIN_ITEM.##COMPARISON(COMPARE) <= 0) {\
					__BIN_LEFT = __BIN_MID + 1;\
				} else {\
					__BIN_RIGHT = __BIN_MID;\
				};\
				__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
			};\
			__BIN_ITEM = COMPTYPE;\
			__BIN_MID = __BIN_ITEM.##COMPARISON(COMPARE) > 0 ? __BIN_MID : __BIN_MID + 1;\
			__BIN_LIST.Insert(__BIN_MID, INPUT);\
		};\
	} while(FALSE)

///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 = length(input)
	switch(total)
		if (0)
			return "[nothing_text]"
		if (1)
			return "[input[1]]"
		if (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]]"

///Checks for specific types in a list
/proc/is_type_in_list(atom/type_to_check, list/list_to_check)
	if(!LAZYLEN(list_to_check) || !type_to_check)
		return FALSE
	for(var/type in list_to_check)
		if(istype(type_to_check, 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)])

///returns a new list with only atoms that are in the typecache list
/proc/typecache_filter_list(list/atoms, list/typecache)
	RETURN_TYPE(/list)
	. = list()
	for(var/thing in atoms)
		var/atom/atom_checked = thing
		if (typecache[atom_checked.type])
			. += atom_checked

///return a new list with atoms that are not in the typecache list
/proc/typecache_filter_list_reverse(list/atoms, list/typecache)
	RETURN_TYPE(/list)
	. = list()
	for(var/atom/atom as anything in atoms)
		if(!typecache[atom.type])
			. += atom

///similar to typecache_filter_list and typecache_filter_list_reverse but it supports an inclusion list and and exclusion list
/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude)
	. = list()
	for(var/atom/atom as anything in atoms)
		if(typecache_include[atom.type] && !typecache_exclude[atom.type])
			. += atom

///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
		var/list/output = list()
		if(only_root_path)
			output[path] = TRUE
		else
			types = ignore_root_path ? subtypesof(path) : typesof(path)
			for(var/T in types)
				output[T] = TRUE
		return output
	else if(islist(path))
		var/list/pathlist = path
		var/list/output = list()
		if(ignore_root_path)
			for(var/current_path in pathlist)
				for(var/subtype in subtypesof(current_path))
					output[subtype] = TRUE
			return output

		if(only_root_path)
			for(var/current_path in pathlist)
				output[current_path] = TRUE
		else
			for(var/current_path in pathlist)
				for(var/subpath in typesof(current_path))
					output[subpath] = TRUE
		return output

/**
 * Removes any null entries from the list
 * Returns TRUE if the list had nulls, FALSE otherwise
**/
/proc/list_clear_nulls(list/list_to_clear)
	var/start_len = list_to_clear.len
	var/list/new_list = new(start_len)
	list_to_clear -= new_list
	return list_to_clear.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/unique_merge_list(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.
 * Will output null sometimes if you use decimals (e.g. 0.1 instead of 10) as rand() uses integers, not floats
**/
/proc/pick_weight(list/list_to_pick)
	var/total = 0
	var/item
	for(item in list_to_pick)
		if(!list_to_pick[item])
			list_to_pick[item] = 1
		total += list_to_pick[item]

	total = rand(1, total)
	for(item in list_to_pick)
		total -= list_to_pick[item]
		if(total <= 0)
			return item

	return null
///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.
/proc/pick_weight_allow_zero(list/list_to_pick)
	var/total = 0
	var/item
	for(item in list_to_pick)
		if(!list_to_pick[item])
			list_to_pick[item] = 0
		total += list_to_pick[item]

	total = rand(0, total)
	for(item in list_to_pick)
		total -= list_to_pick[item]
		if(total <= 0 && list_to_pick[item])
			return item

	return null

/// Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/list_to_pick)
	RETURN_TYPE(list_to_pick[_].type)
	if(list_to_pick.len)
		var/picked = rand(1,list_to_pick.len)
		. = list_to_pick[picked]
		list_to_pick.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/inserted_list)
	var/i
	i = inserted_list.Find(item)
	if(i == inserted_list.len)
		i = 1
	else
		i++
	return inserted_list[i]

/// Returns the previous item in a list
/proc/previous_list_item(item, list/inserted_list)
	var/i
	i = inserted_list.Find(item)
	if(i == 1)
		i = inserted_list.len
	else
		i--
	return inserted_list[i]

///Randomize: Return the list in a random order
/proc/shuffle(list/inserted_list)
	if(!inserted_list)
		return
	inserted_list = inserted_list.Copy()

	for(var/i = 1, i < inserted_list.len, ++i)
		inserted_list.Swap(i, rand(i, inserted_list.len))

	return inserted_list

///same as shuffle, but returns nothing and acts on list in place
/proc/shuffle_inplace(list/inserted_list)
	if(!inserted_list)
		return

	for(var/i = 1, i < inserted_list.len, ++i)
		inserted_list.Swap(i, rand(i, inserted_list.len))

///Return a list with no duplicate entries
/proc/unique_list(list/inserted_list)
	. = list()
	for(var/i in inserted_list)
		. |= i

///same as unique_list, but returns nothing and acts on list in place (also handles associated values properly)
/proc/unique_list_in_place(list/inserted_list)
	var/temp = inserted_list.Copy()
	inserted_list.len = 0
	for(var/key in temp)
		if (isnum(key))
			inserted_list |= key
		else
			inserted_list[key] = temp[key]

///for sorting clients or mobs by ckey
/proc/sort_key(list/ckey_list, order=1)
	return sortTim(ckey_list, order >= 0 ? /proc/cmp_ckey_asc : /proc/cmp_ckey_dsc)

///Specifically for record datums in a list.
/proc/sort_record(list/record_list, field = "name", order = 1)
	GLOB.cmp_field = field
	return sortTim(record_list, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc)

///sort any value in a list
/proc/sort_list(list/list_to_sort, cmp=/proc/cmp_text_asc)
	return sortTim(list_to_sort.Copy(), cmp)

///uses sort_list() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sort_names(list/list_to_sort, order=1)
	return sortTim(list_to_sort.Copy(), 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/bitfield_to_list(bitfield = 0, list/wordlist)
	var/list/return_list = list()
	if(islist(wordlist))
		var/max = min(wordlist.len, 24)
		var/bit = 1
		for(var/i = 1, i <= max, i++)
			if(bitfield & bit)
				return_list += wordlist[i]
			bit = bit << 1
	else
		for(var/bit = 1, bit <= (1<<24), bit = bit << 1)
			if(bitfield & bit)
				return_list += bit

	return return_list

/// Returns the key based on the index
#define KEYBYINDEX(L, index) (((index <= length(L)) && (index > 0)) ? L[index] : null)

///return the amount of items of the same type inside a list
/proc/count_by_type(list/inserted_list, type)
	var/i = 0
	for(var/item_type in inserted_list)
		if(istype(item_type, type))
			i++
	return i

/// Returns datum/data/record
/proc/find_record(field, value, list/inserted_list)
	for(var/datum/data/record/record_to_check in inserted_list)
		if(record_to_check.fields[field] == value)
			return record_to_check
	return null

/**
 * Move a single element from position from_index within a list, to position to_index
 * All elements in the range [1,to_index) before the move will be before the pivot afterwards
 * All elements in the range [to_index, L.len+1) before the move will be after the pivot afterwards
 * In other words, it's as if the range [from_index,to_index) have been rotated using a <<< operation common to other languages.
 * from_index and to_index must be in the range [1,L.len+1]
 * This will preserve associations ~Carnie
**/
/proc/move_element(list/inserted_list, from_index, to_index)
	if(from_index == to_index || from_index + 1 == to_index) //no need to move
		return
	if(from_index > to_index)
		++from_index //since a null will be inserted before from_index, the index needs to be nudged right by one

	inserted_list.Insert(to_index, null)
	inserted_list.Swap(from_index, to_index)
	inserted_list.Cut(from_index, from_index + 1)

/**
 * Move elements [from_index,from_index+len) to [to_index-len, to_index)
 * Same as moveElement but for ranges of elements
 * This will preserve associations ~Carnie
**/
/proc/move_range(list/inserted_list, from_index, to_index, len = 1)
	var/distance = abs(to_index - from_index)
	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(from_index <= to_index)
			return //no need to move
		from_index += len //we want to shift left instead of right

		for(var/i = 0, i < distance, ++i)
			inserted_list.Insert(from_index, null)
			inserted_list.Swap(from_index, to_index)
			inserted_list.Cut(to_index, to_index + 1)
	else
		if(from_index > to_index)
			from_index += len

		for(var/i = 0, i < len, ++i)
			inserted_list.Insert(to_index, null)
			inserted_list.Swap(from_index, to_index)
			inserted_list.Cut(from_index, from_index + 1)

///Move elements from [from_index, from_index+len) to [to_index, to_index+len)
///Move any elements being overwritten by the move to the now-empty elements, preserving order
///Note: if the two ranges overlap, only the destination order will be preserved fully, since some elements will be within both ranges ~Carnie
/proc/swap_range(list/inserted_list, from_index, to_index, len=1)
	var/distance = abs(to_index - from_index)
	if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
		if(from_index < to_index)
			to_index += len
		else
			from_index += len

		for(var/i = 0, i < distance, ++i)
			inserted_list.Insert(from_index, null)
			inserted_list.Swap(from_index, to_index)
			inserted_list.Cut(to_index, to_index + 1)
	else
		if(to_index > from_index)
			var/a = to_index
			to_index = from_index
			from_index = a

		for(var/i = 0, i < len, ++i)
			inserted_list.Swap(from_index++, to_index++)

///replaces reverseList ~Carnie
/proc/reverse_range(list/inserted_list, start = 1, end = 0)
	if(inserted_list.len)
		start = start % inserted_list.len
		end = end % (inserted_list.len + 1)
		if(start <= 0)
			start += inserted_list.len
		if(end <= 0)
			end += inserted_list.len + 1

		--end
		while(start < end)
			inserted_list.Swap(start++, end--)

	return inserted_list

///return first thing in L which has var/varname == value
///this is typecaste as list/L, but you could actually feed it an atom instead.
///completely safe to use
/proc/get_element_by_var(list/inserted_list, varname, value)
	varname = "[varname]"
	for(var/datum/checked_datum in inserted_list)
		if(!checked_datum.vars.Find(varname))
			continue
		if(checked_datum.vars[varname] == value)
			return checked_datum

///remove all nulls from a list
/proc/remove_nulls_from_list(list/inserted_list)
	while(inserted_list.Remove(null))
		continue
	return inserted_list

///Copies a list, and all lists inside it recusively
///Does not copy any other reference type
/proc/deep_copy_list(list/inserted_list)
	if(!islist(inserted_list))
		return inserted_list
	. = inserted_list.Copy()
	for(var/i in 1 to inserted_list.len)
		var/key = .[i]
		if(isnum(key))
			// numbers cannot ever be associative keys
			continue
		var/value = .[key]
		if(islist(value))
			value = deep_copy_list(value)
			.[key] = value
		if(islist(key))
			key = deep_copy_list(key)
			.[i] = key
			.[key] = value

///takes an input_key, as text, and the list of keys already used, outputting a replacement key in the format of "[input_key] ([number_of_duplicates])" if it finds a duplicate
///use this for lists of things that might have the same name, like mobs or objects, that you plan on giving to a player as input
/proc/avoid_assoc_duplicate_keys(input_key, list/used_key_list)
	if(!input_key || !istype(used_key_list))
		return
	if(used_key_list[input_key])
		used_key_list[input_key]++
		input_key = "[input_key] ([used_key_list[input_key]])"
	else
		used_key_list[input_key] = 1
	return input_key

///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]

///Make a normal list an associative one
/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

/// Turns an associative list into a flat list of keys
/proc/assoc_to_keys(list/input)
	var/list/keys = list()
	for(var/key in input)
		keys += key
	return keys

///compare two lists, returns TRUE if they are the same
/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

#define LAZY_LISTS_OR(left_list, right_list)\
	( length(left_list)\
		? length(right_list)\
			? (left_list | right_list)\
			: left_list.Copy()\
		: length(right_list)\
			? right_list.Copy()\
			: null\
	)

///Returns a list with items filtered from a list that can call callback
/proc/special_list_filter(list/list_to_filter, datum/callback/condition)
	if(!islist(list_to_filter) || !length(list_to_filter) || !istype(condition))
		return list()
	. = list()
	for(var/i in list_to_filter)
		if(condition.Invoke(i))
			. |= i

///Returns a list with all weakrefs resolved
/proc/recursive_list_resolve(list/list_to_resolve)
	. = list()
	for(var/element in list_to_resolve)
		if(istext(element))
			. += element
			var/possible_assoc_value = list_to_resolve[element]
			if(possible_assoc_value)
				.[element] = recursive_list_resolve_element(possible_assoc_value)
		else
			. += list(recursive_list_resolve_element(element))

///Helper for /proc/recursive_list_resolve
/proc/recursive_list_resolve_element(element)
	if(islist(element))
		var/list/inner_list = element
		return recursive_list_resolve(inner_list)
	else if(isweakref(element))
		var/datum/weakref/ref = element
		return ref.resolve()
	else
		return element


code:
//wrapper macros for easier grepping
#define DIRECT_OUTPUT(A, B) A <> B
#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image)
#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound)
#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text)
#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text)
#define READ_FILE(file, text) DIRECT_INPUT(file, text)
//This is an external call, "true" and "false" are how rust parses out booleans
#define WRITE_LOG(log, text) rustg_log_write(log, text, "true")
#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false")

//print a warning message to world.log
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].")
/proc/warning(msg)
	msg = "## WARNING: [msg]"
	log_world(msg)

//not an error or a warning, but worth to mention on the world log, just in case.
#define NOTICE(MSG) notice(MSG)
/proc/notice(msg)
	msg = "## NOTICE: [msg]"
	log_world(msg)

//print a testing-mode debug message to world.log and world
#ifdef TESTING
#define testing(msg) log_world("## TESTING: [msg]"); to_chat(world, "## TESTING: [msg]")

GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
// we don't really check if a word or name is used twice, be aware of that
#define testing_profile_start(NAME, LIST) LIST[NAME] = world.timeofday
#define testing_profile_current(NAME, LIST) round((world.timeofday - LIST[NAME])/10,0.1)
#define testing_profile_output(NAME, LIST) testing("[LIST["_PROFILE_NAME"]] profile of [NAME] is [testing_profile_current(NAME,LIST)]s")
#define testing_profile_output_all(LIST) { for(var/_NAME in LIST) { testing_profile_current(,_NAME,LIST); }; };
#else
#define testing(msg)
#define testing_profile_start(NAME, LIST)
#define testing_profile_current(NAME, LIST)
#define testing_profile_output(NAME, LIST)
#define testing_profile_output_all(LIST)
#endif

#define testing_profile_global_start(NAME) testing_profile_start(NAME,GLOB.testing_global_profiler)
#define testing_profile_global_current(NAME) testing_profile_current(NAME, GLOB.testing_global_profiler)
#define testing_profile_global_output(NAME) testing_profile_output(NAME, GLOB.testing_global_profiler)
#define testing_profile_global_output_all testing_profile_output_all(GLOB.testing_global_profiler)

#define testing_profile_local_init(PROFILE_NAME) var/list/_timer_system = list( "_PROFILE_NAME" = PROFILE_NAME, "_start_of_proc"  = world.timeofday )
#define testing_profile_local_start(NAME) testing_profile_start(NAME, _timer_system)
#define testing_profile_local_current(NAME) testing_profile_current(NAME, _timer_system)
#define testing_profile_local_output(NAME) testing_profile_output(NAME, _timer_system)
#define testing_profile_local_output_all testing_profile_output_all(_timer_system)

#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM)
/proc/log_test(text)
	WRITE_LOG(GLOB.test_log, text)
	SEND_TEXT(world.log, text)
#endif

#ifdef REFERENCE_TRACKING_LOG
#define log_reftracker(msg) log_world("## REF SEARCH [msg]")
#else
#define log_reftracker(msg)
#endif

/* Items with ADMINPRIVATE prefixed are stripped from public logs. */
/proc/log_admin(text)
	GLOB.admin_log.Add(text)
	if (CONFIG_GET(flag/log_admin))
		WRITE_LOG(GLOB.world_game_log, "ADMIN: [text]")

/proc/log_admin_private(text)
	GLOB.admin_log.Add(text)
	if (CONFIG_GET(flag/log_admin))
		WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: [text]")

/proc/log_adminsay(text)
	GLOB.admin_log.Add(text)
	if (CONFIG_GET(flag/log_adminchat))
		WRITE_LOG(GLOB.world_game_log, "ADMINPRIVATE: ASAY: [text]")

/proc/log_dsay(text)
	if (CONFIG_GET(flag/log_adminchat))
		WRITE_LOG(GLOB.world_game_log, "ADMIN: DSAY: [text]")

/* All other items are public. */
/proc/log_game(text)
	if (CONFIG_GET(flag/log_game))
		WRITE_LOG(GLOB.world_game_log, "GAME: [text]")

/proc/log_mecha(text)
	if (CONFIG_GET(flag/log_mecha))
		WRITE_LOG(GLOB.world_mecha_log, "MECHA: [text]")

/proc/log_virus(text)
	if (CONFIG_GET(flag/log_virus))
		WRITE_LOG(GLOB.world_virus_log, "VIRUS: [text]")

/proc/log_cloning(text, mob/initiator)
	if(CONFIG_GET(flag/log_cloning))
		WRITE_LOG(GLOB.world_cloning_log, "CLONING: [text]")

/proc/log_paper(text)
	WRITE_LOG(GLOB.world_paper_log, "PAPER: [text]")

/proc/log_asset(text)
	if(CONFIG_GET(flag/log_asset))
		WRITE_LOG(GLOB.world_asset_log, "ASSET: [text]")

/proc/log_access(text)
	if (CONFIG_GET(flag/log_access))
		WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]")

/**
 * Writes to a special log file if the log_suspicious_login config flag is set,
 * which is intended to contain all logins that failed under suspicious circumstances.
 *
 * Mirrors this log entry to log_access when access_log_mirror is TRUE, so this proc
 * doesn't need to be used alongside log_access and can replace it where appropriate.
 */
/proc/log_suspicious_login(text, access_log_mirror = TRUE)
	if (CONFIG_GET(flag/log_suspicious_login))
		WRITE_LOG(GLOB.world_suspicious_login_log, "SUSPICIOUS_ACCESS: [text]")
	if(access_log_mirror)
		log_access(text)

/proc/log_law(text)
	if (CONFIG_GET(flag/log_law))
		WRITE_LOG(GLOB.world_game_log, "LAW: [text]")

/proc/log_attack(text)
	if (CONFIG_GET(flag/log_attack))
		WRITE_LOG(GLOB.world_attack_log, "ATTACK: [text]")

/proc/log_econ(text)
	if (CONFIG_GET(flag/log_econ))
		WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]")

/proc/log_manifest(ckey, datum/mind/mind, mob/body, latejoin = FALSE)
	if (CONFIG_GET(flag/log_manifest))
		WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role.title] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]")

/proc/log_bomber(atom/user, details, atom/bomb, additional_details, message_admins = TRUE)
	var/bomb_message = "[details][bomb ? " [bomb.name] at [AREACOORD(bomb)]": ""][additional_details ? " [additional_details]" : ""]."

	if(user)
		user.log_message(bomb_message, LOG_GAME) //let it go to individual logs as well as the game log
		bomb_message = "[key_name(user)] at [AREACOORD(user)] [bomb_message]"
	else
		log_game(bomb_message)

	GLOB.bombers += bomb_message

	if(message_admins)
		message_admins("[user ? "[ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(user)] " : ""][details][bomb ? " [bomb.name] at [ADMIN_VERBOSEJMP(bomb)]": ""][additional_details ? " [additional_details]" : ""].")

/proc/log_say(text)
	if (CONFIG_GET(flag/log_say))
		WRITE_LOG(GLOB.world_game_log, "SAY: [text]")

/proc/log_ooc(text)
	if (CONFIG_GET(flag/log_ooc))
		WRITE_LOG(GLOB.world_game_log, "OOC: [text]")

/proc/log_whisper(text)
	if (CONFIG_GET(flag/log_whisper))
		WRITE_LOG(GLOB.world_game_log, "WHISPER: [text]")

/proc/log_emote(text)
	if (CONFIG_GET(flag/log_emote))
		WRITE_LOG(GLOB.world_game_log, "EMOTE: [text]")

/proc/log_prayer(text)
	if (CONFIG_GET(flag/log_prayer))
		WRITE_LOG(GLOB.world_game_log, "PRAY: [text]")

/proc/log_pda(text)
	if (CONFIG_GET(flag/log_pda))
		WRITE_LOG(GLOB.world_pda_log, "PDA: [text]")

/proc/log_comment(text)
	if (CONFIG_GET(flag/log_pda))
		//reusing the PDA option because I really don't think news comments are worth a config option
		WRITE_LOG(GLOB.world_pda_log, "COMMENT: [text]")

/proc/log_uplink(text)
	if (CONFIG_GET(flag/log_uplink))
		WRITE_LOG(GLOB.world_uplink_log, "UPLINK: [text]")

/proc/log_spellbook(text)
	if (CONFIG_GET(flag/log_uplink))
		WRITE_LOG(GLOB.world_uplink_log, "SPELLBOOK: [text]")

/proc/log_codex_ciatrix(text)
	if (CONFIG_GET(flag/log_uplink))
		WRITE_LOG(GLOB.world_uplink_log, "CODEX: [text]")

/proc/log_telecomms(text)
	if (CONFIG_GET(flag/log_telecomms))
		WRITE_LOG(GLOB.world_telecomms_log, "TCOMMS: [text]")

/proc/log_chat(text)
	if (CONFIG_GET(flag/log_pda))
		//same thing here
		WRITE_LOG(GLOB.world_pda_log, "CHAT: [text]")

/proc/log_vote(text)
	if (CONFIG_GET(flag/log_vote))
		WRITE_LOG(GLOB.world_game_log, "VOTE: [text]")

/proc/log_shuttle(text)
	if (CONFIG_GET(flag/log_shuttle))
		WRITE_LOG(GLOB.world_shuttle_log, "SHUTTLE: [text]")

/proc/log_topic(text)
	WRITE_LOG(GLOB.world_game_log, "TOPIC: [text]")

/proc/log_href(text)
	WRITE_LOG(GLOB.world_href_log, "HREF: [text]")

/proc/log_mob_tag(text)
	WRITE_LOG(GLOB.world_mob_tag_log, "TAG: [text]")

/proc/log_sql(text)
	WRITE_LOG(GLOB.sql_error_log, "SQL: [text]")

/proc/log_qdel(text)
	WRITE_LOG(GLOB.world_qdel_log, "QDEL: [text]")

/proc/log_query_debug(text)
	WRITE_LOG(GLOB.query_debug_log, "SQL: [text]")

/proc/log_job_debug(text)
	if (CONFIG_GET(flag/log_job_debug))
		WRITE_LOG(GLOB.world_job_debug_log, "JOB: [text]")

/* Log to both DD and the logfile. */
/proc/log_world(text)
#ifdef USE_CUSTOM_ERROR_HANDLER
	WRITE_LOG(GLOB.world_runtime_log, text)
#endif
	SEND_TEXT(world.log, text)

/* Log to the logfile only. */
/proc/log_runtime(text)
	WRITE_LOG(GLOB.world_runtime_log, text)

/* Rarely gets called; just here in case the config breaks. */
/proc/log_config(text)
	WRITE_LOG(GLOB.config_error_log, text)
	SEND_TEXT(world.log, text)

/proc/log_mapping(text)
	WRITE_LOG(GLOB.world_map_error_log, text)

/proc/log_perf(list/perf_info)
	. = "[perf_info.Join(",")]\n"
	WRITE_LOG_NO_FORMAT(GLOB.perf_log, .)

/**
 * Appends a tgui-related log entry. All arguments are optional.
 */
/proc/log_tgui(user, message, context,
		datum/tgui_window/window,
		datum/src_object)
	var/entry = ""
	// Insert user info
	if(!user)
		entry += "<nobody>"
	else if(istype(user, /mob))
		var/mob/mob = user
		entry += "[mob.ckey] (as [mob] at [mob.x],[mob.y],[mob.z])"
	else if(istype(user, /client))
		var/client/client = user
		entry += "[client.ckey]"
	// Insert context
	if(context)
		entry += " in [context]"
	else if(window)
		entry += " in [window.id]"
	// Resolve src_object
	if(!src_object && window?.locked_by)
		src_object = window.locked_by.src_object
	// Insert src_object info
	if(src_object)
		entry += "\nUsing: [src_object.type] [REF(src_object)]"
	// Insert message
	if(message)
		entry += "\n[message]"
	WRITE_LOG(GLOB.tgui_log, entry)

/* For logging round startup. */
/proc/start_log(log)
	WRITE_LOG(log, "Starting up round ID [GLOB.round_id].\n-------------------------")

/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */
/proc/shutdown_logging()
	rustg_log_close_all()

/* Helper procs for building detailed log lines */
/proc/key_name(whom, include_link = null, include_name = TRUE)
	var/mob/M
	var/client/C
	var/key
	var/ckey
	var/fallback_name

	if(!whom)
		return "*null*"
	if(istype(whom, /client))
		C = whom
		M = C.mob
		key = C.key
		ckey = C.ckey
	else if(ismob(whom))
		M = whom
		C = M.client
		key = M.key
		ckey = M.ckey
	else if(istext(whom))
		key = whom
		ckey = ckey(whom)
		C = GLOB.directory[ckey]
		if(C)
			M = C.mob
	else if(istype(whom,/datum/mind))
		var/datum/mind/mind = whom
		key = mind.key
		ckey = ckey(key)
		if(mind.current)
			M = mind.current
			if(M.client)
				C = M.client
		else
			fallback_name = mind.name
	else // Catch-all cases if none of the types above match
		var/swhom = null

		if(istype(whom, /atom))
			var/atom/A = whom
			swhom = "[A.name]"
		else if(istype(whom, /datum))
			swhom = "[whom]"

		if(!swhom)
			swhom = "*invalid*"

		return "\[[swhom]\]"

	. = ""

	if(!ckey)
		include_link = FALSE

	if(key)
		if(C?.holder && C.holder.fakekey && !include_name)
			if(include_link)
				. += ""
			. += "Administrator"
		else
			if(include_link)
				. += ""
			. += key
		if(!C)
			. += "\[DC\]"

		if(include_link)
			. += ""
	else
		. += "*no key*"

	if(include_name)
		if(M)
			if(M.real_name)
				. += "/([M.real_name])"
			else if(M.name)
				. += "/([M.name])"
		else if(fallback_name)
			. += "/([fallback_name])"

	return .

/proc/key_name_admin(whom, include_name = TRUE)
	return key_name(whom, TRUE, include_name)

/proc/loc_name(atom/A)
	if(!istype(A))
		return "(INVALID LOCATION)"

	var/turf/T = A
	if (!istype(T))
		T = get_turf(A)

	if(istype(T))
		return "([AREACOORD(T)])"
	else if(A.loc)
		return "(UNKNOWN (?, ?, ?))"


code:
#define pick_list(FILE, KEY) (pick(strings(FILE, KEY)))
#define pick_list_weighted(FILE, KEY) (pick_weight(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]")